views:

445

answers:

1

I came across a nice one-liner to search for text and print out the results with a given number of trailing and following lines. I'm trying to create a script out of this. So far this is what I have:

# *********************************************************
# This uses nawk to list lines coming prior to and after
# the given search string.
# Format: xgrep <string> <file> <before> <after>
# ********************************************************

STR=$1
FILE=$2
BEF=$3
AFT=$4
export STR
export FILE
export BEF
export AFT

nawk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)print r[(NR-c+1)%b];print;c=a}b{r[NR%b]=$0}' b=$BEF a=$AFT s=$STR $FILE

The problem is that the output of the nawk command doesn't appear on the screen

>xgrep "bin/sh" * 0 3
>

But if I type in the command, I get a proper output:

>nawk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)print r[(NR-c+1)%b];print;c=a}b{r[NR%b]=$0}' b=0 a=3 s="bin/sh" *
#!/bin/sh

export AEGIS_PROJECT=$1
export EDITOR=cat
#!/bin/sh

aegis -cpu $pwd
aegis -dbu $1
#!/bin/sh
cd $HOME/ex_7/src/DTTCom
NRMan alphabuild clean
NRMan alphabuild library ex_7 8.19 app TEST DTTCom debug -j10
#!/bin/sh
cd $HOME/icx/src/DTTCom
NRMan alphabuild clean
NRMan alphabuild library icx_1 1.1 app TEST DTTCom debug -j10
#!/bin/sh
# *********************************************************
# This uses nawk to list lines coming prior to and after
# the given search string.

What's the reason for this and how can I get the script to work?

+2  A: 

Try using:

xgrep "bin/sh" '*' 0 3

instead.

The expansion of the wildcard term is happening in the current shell before the arguments are passed to your script as shown in this transcript:

pax: xgrep include *.c 0 3
#include <stdio.h>
gawk: (FILENAME=binmath.c FNR=1) fatal: division by zero attempted in '%'

pax: xgrep include '*.c' 0 3
#include <stdio.h>

// Prototypes - should go in separate header file.
void compBinNum (char*);
#include <stdio.h>
#include <string.h>
#include <string.h>

#define UTF8ERR_TOOSHORT -1
#define UTF8ERR_BADSTART -2
#include <stdio.h>
#include <errno.h>
#include <errno.h>

int main (void) {
    FILE *file = fopen ("words.txt", "r");

You can see how these arguments work with the following simple script and output:

pax: cat argtest
    #!/bin/bash
    echo Number of arguments: $#
    echo Arguments: "$*"

pax: argtest *
    Number of arguments: 32
    Arguments: Makefile a.exe argtest binmath.c binmath.exe dev
        file.db infile input.txt inputfile limit10 output.txt
        p2.sh p2expected p2input1 p2input2 qq qq.c qq.cpp qq.exe
        qq.exe.stackdump qq.pl qq.py qqin qqq.c qqq.s tmpfile
        words2.txt xgrep xx.c xx.exe xx.pl

pax: argtest '*'
    Number of arguments: 1
    Arguments: *

Update:

Based on your question in the comments:

Thanks. It worked when I wrapped the file with single quotes. Is there a way that I could do this inside the script so that the user doesn't have to bother with typing single quotes?

No, that's because the shell is doing it before your script ever sees it. However, if you move the file specification to the end of the command line thus:

xgrep include 0 3 *.c

you could modify your script to not just process argument number 4 but every argument after that as well, one at a time. Then, when they've been expanded by the shell, it won't matter.

Something like (with the gawk/nawk on a single line):

STR=$1
BEF=$2
AFT=$3
while [[ ! -z "$4" ]] ; do
    echo ========== "$4"
    gawk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)
        print r[(NR-c+1)%b];print;c=a}b{r[NR%b]=$0}'
        b=$BEF a=$AFT s=$STR "$4"
        | sed "s/^/$4: /"
    shift
done
echo ==========

Letting the shell handle your expansion and using a loop also allows you to do tricks such as printing the file name with each block (or line) of output:

pax: xgrep include  0 3 *.c
    ========== binmath.c
    binmath.c:     #include <stdio.h>
    binmath.c:
    binmath.c:     // Prototypes - should go in separate header file.
    binmath.c:     void compBinNum (char*);
    ========== qq.c
    qq.c:     #include <stdio.h>
    qq.c:     #include <string.h>
    qq.c:     #include <string.h>
    qq.c:
    qq.c:     #define UTF8ERR_TOOSHORT -1
    qq.c:     #define UTF8ERR_BADSTART -2
    ========== qqq.c
    ========== xx.c
    xx.c: #include <stdio.h>
    xx.c: #include <errno.h>
    xx.c: #include <errno.h>
    xx.c:
    xx.c: int main (void) {
    xx.c:     FILE *file = fopen ("words.txt", "r");
    ==========
paxdiablo
Thanks. It worked when I wrapped the "file" with ''. Is there a way that I could do this inside the script so that the user doesn't have to bother with typing single quotes?
Gayan
See update, @Gayan.
paxdiablo
How can I change "sed "s/^/$4: /" to print the line number of the file?
Gayan
sed can't do it since that information has already disappeared, you would have to generate it with awk. Off the top of my head, the section "b{r[NR%b]=$0}" would become something like "b{r[NR%b]=NR + "> " + $0}" (untested syntax, I don't have access to awk from current location).
paxdiablo