2

When use eval on a command, does eval apply the shell twice to the redirection part of the command?

Suppose the filename in the redirection contains a whitespace, if eval applies all the steps of parsing, shell expansions and word splitting and other steps to it more than once, the filename will be split into two words during the word splitting step in the second round.

Does the following example imply that eval doesn't apply the shell twice to the redirection part of the following command, so the filename is not split into two words?

$ filename="my file"
$ eval "cat" < "$filename"
hi
1

In the example you provide, the only thing eval is getting is cat, the redirection is happening outside the eval and the file is being provided as stdin for the eval "cat" command.

One variation is to quote the whole command, including the redirection, using single quotes:

$ eval 'cat < "$filename"'
hi

Now eval is getting the whole string, including the redirection and the variable name, so it's doing variable expansion and the needed quoting for the filename with spaces. This would still work.

Another option is using double quotes for the string:

$ eval "cat < '$filename'"
hi

Now the variable is expanded by the shell, but this still works since the quotes inside it keep the filename together. (Note that this would break if the filename contains an apostrophe, though.)

What would *not" work is this:

$ eval "cat" "<" "$filename"

This is similar to your example, but with the < quoted, the redirection will not be executed by the external shell. eval will then put together the arguments, and the resulting command will be:

cat < my file

Which will not work as expected, since the quotes around my file are now gone...

1

The way eval works is by constructing the arguments together as one shell command to run. Since your variable filename is quoted, it will not undergo word-splitting when this command is constructed to run.

The same may not apply for a case of your filename with spaces and an unquoted expansion will cause an error. For e.g.

filname=my\ file
echo "dude" > my\ file
eval cat < $filename
bash: $filename: ambiguous redirect

Notice the word-splitting done here and cat receives an incorrect filename for input redirection causing the error.

Also notice that, if not for the re-direction when using cat, if used on the file directly, eval will make cat open two files in this case, i.e.

eval cat $filename

will translate into running

cat my file

E.g. if you have file named file in your current directory, it will display its contents and show file not present for the file named my on error stream.

filename=my\ file
echo "foo" > file

We are making the file my to be not present

eval cat $filename 2>&1
cat: my: No such file or directory
foo

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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