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 have 8 files, and each contains around 2000 lines. I want to search the particular word in these files between line number 1500 to 2500.

The output should look like:

sample_1.txt :

1510:declare var testing


sample_2.txt :

1610:declare var testing


sample_7.txt :

1610:declare var testing


sample_10.txt :

1710:declare var testing

Is it possible to use grep for this task?

share|improve this question

4 Answers 4

up vote 1 down vote accepted

Try this:

#!/usr/bin/awk -f
BEGIN {
    begin = ARGV[1]
    end = ARGV[2]
    pattern=ARGV[3]
    ARGV[1] = ARGV[2] = ARGV[3] = ""
}

NR > end {exit}

NR == 1 {
    print FILENAME " :\n"
}

NR >= begin {
    if ($0 ~ pattern) 
        print NR ":" $0
}

Call it like this:

./rangegrep 1500 2000 'declare var testing' sample*.txt

The search string can be a regular expression.

Edit:

I changed from range checking the line number to using exit as in akira's answer since the exit will stop processing lines at the end of the range and save time by not reading the rest of the lines in the file.

share|improve this answer
    
is it possible with using grap instead of program, –  bharanikumar Jan 25 '11 at 8:43
    
@Dennis Williamson: 'print FILENAME' could be put into the BEGIN { } as well... @bharanikumar: why are you so obsessed with grep? use the right tool for the problem and do not try to use the one hammer you have found somewhere and now everything you see is nails. –  akira Jan 25 '11 at 9:21
1  
@akira: From man gawk: "FILENAME is undefined inside the BEGIN block (unless set by getline)." @bharanikumar: You could do it with grep if you used head and tail, but you'd lose line numbering and filename labeling. –  Dennis Williamson Jan 25 '11 at 15:43
    
Though it's more efficient to write this as one script I can't help but thinking that this is a missing tool. As a complement to head and tail, a tool that gives you a specific range of lines or bytes out of the middle of a stream (based on offset from the start). We could call it torso or thorax. –  phogg Jan 25 '11 at 15:56
1  
@phogg: I like "torso"! However, "thorax" excludes the abdomen and we can't do that because it's the "guts" of the file that we're really interested in. Having a tool like "torso" probably violates at least one principle in the Unix philosphy since head|tail does the job adequately. –  Dennis Williamson Jan 25 '11 at 16:22

awk does what you want:

% awk 'NR < 1500 { next }; NR > 2500 { exit}; \
    /pattern/ { printf("%s:\n%d:%s\n", FILENAME, NR, $0); }' \
    sample_*.txt

to have as much space as you provided in your desired output, you just have to add as many \n to the printf statement...

share|improve this answer
    
I upvoted your answer earlier, but forgot to leave this comment. I like your next and exit better than my range checking. The exit can speed things up quite a bit under some circumstances. I'm going to blatantly copy those into my answer. OK, fine, with attribution. ;) –  Dennis Williamson Jan 25 '11 at 15:47

Without using awk how about some shell script + sed:

for f in sample_*.txt ; do echo "$f : " ; \
    sed -ne '1500,2500{/pattern/{=;p}}' $f ; \
    echo ; \
done
share|improve this answer

Purely In The Interests Of Science, I present an implementation of torso, the logical middle between head and tail.

In practice, as others have noted, this is really unnecessary since you can get the desired output yourself by a trivial combination of head and tail.

#!/bin/sh

usage () {
    printf "$0: $0 [-c <byte> -C <byte>] [-n <line> -N <line>] file [file ... ]\n"
}

while [ $# -gt 0 ] ; do
    case "$1" in
            -c|--byte-start) shift ; start="$1" ; mode=byte ; shift ;;
            -C|--byte-end) shift ; end="$1" ; mode=byte ; shift ;;
            -n|--line-start) shift ; start="$1" ; mode=line ; shift ;;
            -N|--line-end) shift ; end="$1" ; mode=line ; shift ;;
                --) shift ;;
            -*) printf "bad option '%s'\n" "$1" ; usage ; exit 201 ;;
                *) files=("${files[@]}" "$1") ; shift ;;
         esac
done

if [ $start -gt $end ] ; then
    printf "end point cannot be before start point\n"
    usage
    exit 202
fi

head_cmd=
tail_cmd=
end=$((end - start))
if [ $mode = "line" ] ; then
    head_cmd="-n $end"
    tail_cmd="-n +$start"
elif [ $mode = "byte" ] ; then
    head_cmd="-c $end"
    tail_cmd="-c +$start"
fi

if [ ${#files[@]} -eq 0 ] ; then
    cat - | tail $tail_cmd | head $head_cmd
else
    tail $tail_cmd "${files[@]}" | head $head_cmd
fi

To keep it topical, here's how to use torso to solve the question:

torso -n 1500 -N 2500 input_file | grep -n "test"

Or for output conforming to the requirements

for file in sample_{1,2,7,10} ; do
     printf "\n\n%s:\n\n" "$file"
     torso -n 1500 -N 2500 "$file" | grep -n "test"
done

You may begin your criticisms... now!

share|improve this answer
    
You could put cat -n in there somewhere to get line numbering. –  Dennis Williamson Jan 25 '11 at 20:10
    
I worry about the possibility of false positives in the grep match if I do that. Probably it should be an additional switch. What "line number" means when there are multiple files is also a question. –  phogg Jan 25 '11 at 22:59
    
@Dennis would have made sure I saw your comment. I just happened upon it. –  Dennis Williamson Jan 26 '11 at 1:32

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.