3

I have a bash script where I want to do this

    (in pseudo)
    for each item in excludelist
        append to string: | grep -v $item

    for each file in directory 
    do
        stuff
    done

and now

   (in real code, the creation and concatenation of f works, the problem is not here I believe)
   for i in $EXCLUDE
   do
       echo $i
       f="$f | grep -v $i"
   done

   for d in `ls $AWSTATSCONF/awstats*conf $f`
   do
       echo $d
   done

output

   ls: |: No such file or directory
   ls: grep: No such file or directory

Any and all help really appreciated to get this to work.

Greetings,

Shadow

ShadowFlame
  • 2,996
  • 5
  • 26
  • 40

5 Answers5

2

Since you're using bash, you can use pattern matching to exclude your EXCLUDE list:

pattern=$(awk ' BEGIN {OFS="|"; printf("!(")} 
                {$1=$1; printf("%s",$0)} 
                END {print ")"}
              ' <<< $EXCLUDE)
shopt -s extglob
for d in $pattern; do
  echo $d
done    
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
2

There are many ways to deal with it.

There are, rightly, misgivings about parsing the output of ls. How justified those are depends on whether you have to deal with all possible file names (ones containing spaces and newlines are particularly problematic), or whether you have normal file names that use just the portable filename character set (which, according to POSIX, are the (Latin) alphabet, the digits, plus ., -, and _).

Assuming that you only have to deal with portable filenames, then you could get the effect you wanted with:

for i in $EXCLUDE
do
    echo $i
    f="$f | grep -v $i"
done

for d in `eval ls $AWSTATSCONF/awstats*conf $f`
do
    echo $d
done

Using eval like that forces the shell to treat the pipes in the string $f as pipes in the shell. Beware: eval is a powerful and therefore dangerous tool.

The chosen mechanism of running 20 greps in sequence if there are 20 terms to exclude uses a lot of processes. It probably won't be too serious for demonstration purposes, but if you're dealing with production work and thousands of files...not so good.

You'd probably want to look at building an egrep (or grep -E) command:

f="antidisestablishmentarianism"
for i in $EXCLUDE
do
    f="$f|$i"
done

ls $AWSTATSCONF/awstats*conf |
egrep -v "$f" |
while read d
do echo $d
done

I assume you don't have any stats files with names containing 'antidisestablishmentarianism' lurking around (yes, it's a real word; it is mostly used here as a joke, though).

The while read formulation is another way of writing the for loop. Be wary of subprocesses not being able to affect the parent shell's variables. That is, the while loop is processed by a sub-shell, not the main shell, so if you are relying on the loop to set variables in the main shell, it won't work — and you can/should go back to the for loop. However, you don't need eval again:

for d in $(ls $AWSTATSCONF/awstat*conf | egrep -v "$f")
do
    echo $d
done

Using $(...) instead of back-quotes is generally a good idea.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

The problem is that foo whatever whatever else is executed as plain escaped arguments to the ls command (of course | and grep are not found on the current directory). eval is the command that executes a string as a bash command

You need to do something like the following

for d in `eval "ls $AWSTATSCONF/awstats*conf $f"`
do
   echo $d
done

Keep in mind that this opens a big can of worms security wise, check the following FAQ to understand more and for other alternatives

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • no problem. it is for internal use by professionals only. no web frontend or the likes. but thanks for the warning though :-) – ShadowFlame May 07 '12 at 09:26
0

Don't use for i in $(ls). You should use globbing.

Don't use concatenation to build a long string of grep -v.

Where does $EXCLUDE come from? It should be an array instead of a string.

for d in "$AWSTATSCONF"/awstats*conf
do
    for e in "${exclude[@]}"
    do
        if [[ $d != $e ]]
        then
            echo "$d"
        fi
    done
done
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
-1

You need to append the *output of grep to $f.

$f = "$f $(grep -v $i)"
Benjamin Bannier
  • 55,163
  • 11
  • 60
  • 80
  • I believe I have misformulated my question. The part with the problem I believe is the ls, not the creation of the greps.. – ShadowFlame May 07 '12 at 08:31