Help using variable in find rule

I'm not able to use a variable in my find rule. It's essentially being ignored.

I'm trying to store a list of file types to ignore in a variable.

This is the relevant code.

#!/bin/ksh

EXCEPTIONS='-not -name "*.xom" -a -not -name "*.sh" -a -not -name "*.pl"'

/usr/local/bin/find path/to/files $EXCEPTIONS > FileList.txt

When I use the contents of the EXCEPTIONS variable directly, the find rule works. However the variable doesn't work at all? Is this even possible?

Try:

eval /usr/local/bin/find path/to/files $EXCEPTIONS > FileList.txt

--
Bye

Thank you so much. I was putting my eval in the wrong place.

The short answer is, you cannot put quotes in quotes that way directly, the shell won't do that kind of doublethink. You asked for literal quotes, so it gave you literal quotes; making them go away takes an eval, which means big trouble. If someone manages to cram `rm -RF ~/ into your string, eval will execute that!

I'd do this instead:

set -- "-not" "-name" "*.xom" "-a" "-not" "-name" "*.sh" "-a" "-not" "-name" "*.pl"

find ... "$@"

This lets you do splitting on arguments instead of spaces, preserving your strings literally without having to deal with quotes inside quotes. This avoids the eval, and makes your code much safer.

1 Like

Very true! The shell maintains literally a switch, which flips between "inside quotes" and "outside quotes". Once it encounters a quotation mark, the switch flips into the other position. Per default it is on "outside a quotation". If the shell encounters the first quotation mark the switch flips into "inside quotes" and everything is interpreted accordingly, once it encounters the second quote the switch flips back to "outside quotes" again.

This is why quotes cannot be nested in any way.

I hope this helps.

bakunin

2 Likes

Uhm... How can it be done without having write access to the script?

Because, if someone has write access to the script, she can modify it as she likes (regardless of the presence of eval).
--
Bye

Because the command is being run from a variable. I'm guessing he didn't do that on a lark, but because he wanted the contents to vary depending on how the script is used.

That leaves room for injection of malicious strings unless done very, very carefully.

It's also completely avoidable by just not using eval at all.

Let's try to be mor specific. It's interesting, I'm curious about it. :slight_smile:

The only way I'm able to think of is that of a parameter automatically built reading things (maliciously writable by others). For example, let's say I want to discard all files having the same extension of the files in the current directory.

lem@biggy:/tmp/dir$ ls -a1
.
..
a.txt
b.sh
c.pdf
malicious.; ls ~
lem@biggy:/tmp/dir$ for a in *.* ; do arr+=( "-not -name \*.${a##*.}" ) ; done
lem@biggy:/tmp/dir$ echo ${arr[@]}
-not -name \*.txt -not -name \*.sh -not -name \*.pdf -not -name \*.; ls ~
lem@biggy:/tmp/dir$ eval find /path ${arr[@]}
 ... funny output with ls, disaster with rm ...

So is this what you're fearing? Are there other noticeable and completely different dangers? Thanks. :slight_smile:
--
Bye

How about a file named `rm -Rf ~/` ? That's a perfectly valid filename. Feed that filename into an eval and it will be executed.

If you quote it defensively, so what? They can put quotes and spaces in the filename to unquote it.

It needn't even be malicious. People can generate weird filenames by accident sometimes, and anything resembling shell syntax is something eval will try to parse, potentially causing syntax errors or worse. Eval can parse any shell syntax, so the only limit is your imagination.

And it's completely avoidable. Eval is not needed here.

I changed my code as follows

#!/bin/ksh

set -- "-not" "-name" "*.xom" "-a" "-not" "-name" "*.sh" "-a" "-not" "-name" "*.pl"

/usr/local/bin/find path/to/files $@ > FileList.txt

It is working beautifully and it sounds like it will be much safer to run. Thanks for your help.