[TriLUG] bash weirdness

Kevin Hunter hunteke at earlham.edu
Wed Apr 8 11:36:52 EDT 2009


At 9:21am -0400 on Tue, 07 Apr 2009, Stan Briggs wrote:
> the answer was simple but crude. eval and exec each had their own subtleties
> and quirks. instead, i manually spawn my own bash sub-process and provide it
> the line that i want executed as a command line parameter. like this:
> 
> line="dpkg -l > all-packages.txt"
> /bin/bash -c "$line"
>
> this did exactly what i wanted it to do.
>
> any feedback on the cons of doing it this way would be greatly appreciated.

You're effectively doing the work in a subshell.  If that's what you
want, either for security (as in execute it as a different user), or you
aren't sure if the command will do something naughty, then go for it.

The possible cons are speed and memory, in that you have to allocate yet
another program.  If this is just a sysadmin type job, this will likely
not be your bottleneck or problem area, so don't worry about it.  It
passes the squint test -- you've solved the problem; move on.

> also, i never really got any explanation on any links that explained why the
> redirections caused issues.

However, since you ask ... ;-)

> [From previously, why didn't these work?]
> $ $line
> $ `$line`
> $ output=`$line` 

I believe the issue is tokenization.

With `$line`, the subshell is splitting the cmd and arguments by
whitespace, so

$0 = dpkg
$1 = -l
$2 = >
$3 = all-packages.txt

Then, when it runs $0, it's effectively as if you had typed:

$ "dpkg" "-l" ">" "all-packages.txt"

In other words, the subshell doesn't interpret $2 because it thinks it's
just another argument to dpkg, as specified by the implicit quotes.

Executing $line on it's own is the same thing, but now within the
current environment, not a subshell.

The exec command isn't what you want either, because it will actually
replace the current shell with whatever command you specify.  As an
example, open a *new* shell window and try this:

$ exec ls

Note that the shell window immediately closes or logs out.  Actually,
the ls binary replaced the bash binary in memory, ran (displayed
output), then, because it was finished, whatever the parent of bash was
cleaned up.  In my GUI gnome-terminal, this means that the window just
goes away.  Try this on a tty (Ctrl+Alt+F1), for instance.

The bash command 'eval' should get you where you want to go, however.
It executes the entire line as a command in it's own instance (i.e. no
subshell).

---
#/bin/bash

line="dpkg -l > all-packages.txt"

eval "$line"
---

If you want it executed in a subshell, then the bash -c command you
found is exactly what you want.  Or, if you want some syntactic sugar:

$ `eval $line`

To see more of what's going on, you can utilize the $BASH_SUBSHELL
variable.  Try replacing $line with:

line="dpkg -l > all_pkgs.txt; echo At subshell level: \$BASH_SUBSHELL"

Note the escape (\$) for BASH_SUBSHELL.  Mas importante.

Cheers,

Kevin



More information about the TriLUG mailing list