Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'd like show a certain line or lines of a file with context, kind of like a unified diff, on the command line in Linux:

$ (something) -l 154 stuff.py

   150:  def foo(bar):
   151:    """
   152:    Does the thing.
   153:    """
>> 154:    x = some_calculation()
   155:    y = something_else()
   156:    x.baz(y)
   157:    return x.quux()

Is there an easy way to do this?

share|improve this question
up vote 7 down vote accepted

Often times when I'm searching for something in grep, I'll turn on the context option (-C). So if you want context based on searching for a keyword you can do it like this:

grep -C3 searchpattern file

That will give you 3 lines above and 3 below of context. Increase/decrease as necessary. You can also go specific amounts above and below with -A number (above) and -B number (below). If you want to do it specifically by line number, you could try the nl command for numbering the lines then grep -C3 based on matching the number. For instance to get line 26:

nl -n ln -ba inputfile | grep -C3 "^26 "

Its a bit tricky though because you have to pass those formatting options to nl to get it to not put tabs and stuff in the numbering format. grep also has a line numbering option (-n) and you could pass things through grep twice like this based on matching nothing first (the double-double quotes) then the line number second:

grep -n "" inputfile | grep -C3 "^26:"

Just make sure you use some context in your regex for matching the line number so that you don't also match things like 126 or 260, etc.

share|improve this answer
    
I like giving the line numbers. – jwernerny Feb 24 '11 at 20:47
    
I like this one because I can remember it easily. I didn't know about the nl command. Thanks! – a paid nerd Feb 25 '11 at 15:32

Try this

head -157 <filename> | tail -7

A little arithmetic required, but basically the idea is:

head -<line number + trailing lines> | tail -<total lines to see>
share|improve this answer
1  
An elegant solution. – deltaray Feb 24 '11 at 18:44

gawk:

awk -v L=154 'NR==L { printf ">> %5s: ", NR; print $0 ; next } (L-3 <= NR) && (NR <= L+3) { printf "   %5s: ", NR; print $0 }' stuff.py

Just stick it in a shell script.

share|improve this answer
    
+1 for giving line numbers – jwernerny Feb 24 '11 at 20:47
    
Nice.. I'm starting to be impressed by awk (I've never used it beyond a snippet or two).. It would be perfect if the line numbers were left padded... The data columns misalign a 10, 100... – Peter.O Feb 26 '11 at 0:09
    
@fred.bear: Should be fixed now. – Ignacio Vazquez-Abrams Feb 26 '11 at 0:22

Yes, there are several ways to do this. In bash:

line=154 ; offset=4 ; ((start=line-offset)) ; ((end=line+offset)) ; cat -n stuff.py | sed -n "$start,$end p"

You could put this all in a function or script.

share|improve this answer

This uses nl to add configurable line numbers
Args 1 and 2 are as per your question (but no -l)
Args 3 and 4 are the number of lead and trail lines (default=3)

[[ "$1" == "" ]] && lnum=0 || lnum=$(($1)); ((lnum==0)) && echo "ERROR: Line number: $1" &&  exit 1 
[[ ! -f "$2"  ]] && echo "ERROR: Input file: $2" && exit 2 || file="$2"
[[ "$3" == "" ]] && lead=3 || ((lead=$3)) # default 
[[ "$4" == "" ]] && tail=3 || ((tail=$4)) # default
((lnum<=lead)) && ((lead=lnum-1))
mesg="/tmp/$USER.$(basename $0).mesg"; [[ -f "$mesg" ]] && rm "$mesg"
marg=": "
mark=">>"
nl -s "$marg" "$file" \
| sed -n "$((lnum-lead)),$((lnum+tail)) p" | tee >(wc -l >"$mesg") \
| sed "s/^\([ ]*$lnum\)$marg\(.*\)/\1$mark\2/"
lnct=$(cat "$mesg"); rm "$mesg"
((lnct<((lead+tail+1)))) && echo "Waring: EOF"

Output

  21: 
  22: class InsertTimestampPlugin(gedit.Plugin):
  23:   def __init__(self):
  24>>    gedit.Plugin.__init__(self)
  25: 
  26:   def insert_datetime(self, action, window):
  27:     doc  = window.get_active_document()
share|improve this answer
    
C'mon, this what Perl was invented for ;) --- perl -lne'printf "%-3s %5s $_\n", $. == 42 ? ">>" : "", $.' – a paid nerd Feb 25 '11 at 15:36
    
@a paid nerd: I think the button you were looking for is labeled "Add Another Answer" – Peter.O Feb 25 '11 at 21:14
    
@a paid nerd: I just relized that this is your question... and now I am a bit puzzled why you asked the question if you already had a perl solution... and if you are referring to the length of the entry, it's working part can be put on a single line.. the rest is for parameter validation.. and it is spread out for clarity... (now with warning messages) ... Aside from the -l, What I've presented is what you asked for: (something) -l 154 stuff.py – Peter.O Feb 25 '11 at 23:04
    
I've looked further into the sed/awk/perl examples. The perl one dumps the entire file, so I made awk do exactly the same (all I/O >/dev/null). On a scale of 1-42, re time-to-execute 1000 itterations, awk scored 42, perl 41.. sed, with a score of 6, can't handle line numbers in a single pass, and right-justifying is a pain, so it's not suited to this particular task. Today is the first time I've done anything with a perl script, and almost likewise with awk). I quite like the terse nature of perl, but I'll go with awk (too many concurrent learning curves :) – Peter.O Feb 26 '11 at 13:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .