Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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
add comment

5 Answers

up vote 6 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
add comment

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
add comment

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
add comment

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
add comment

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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.