The following code works fine
#!/bin/bash
while true; do
echo $(pwd | awk '{print "dir: "$1}')
sleep 1
done
But I need the command to be in a variable, like this.
#!/bin/bash
COMMAND=pwd | awk '{print "dir: "$1}'
while true; do
echo $($COMMAND)
sleep 1
done
The first snippet executes fine, and prints something like dir: /present/directory. The second snippet just prints blank lines. Why is that and how do I fix it?
There’s no significance in the commands I used. I just needed to use a pipe and chose these commands to demonstrate it.
If you like living dangerously, the answer is
eval:Why dangerously? It’s hard to control what’s going on with
eval, doubly so if you accept the command string from a naïve (let alone malicious) user. Also note how carefully I had to quote the string containing the command.Why didn’t version 2 in the question work?
This line doesn’t do what you think it does, put bluntly.
It is a pipeline, with the first ‘command’ being the string assignment
COMMAND=pwdtreated as an environment variable for the empty command, which sends no data toawk. After completion, the value in$COMMANDis an empty string (both because of the sub-shell used with the pipeline and because theCOMMAND=pwdonly applied while the (empty) command was executed), so the loop echoes the result of executing the empty string — which is also an empty string. Hence the line of blanks.Ouch, and Yuck!
If you had a tab in the source between
$8and$9,awkwould treat it as generic white space and paste$8and$9together in the output. To get a tab in the output, you’ll need either a quoted string containing a tab, or a quoted string containing\tin between the two fields, or you’ll use aprintf()with a suitable format string.So, in principle, you’ll probably want:
I’ve switched to single quotes around the string because they’re mostly easier to understand. Whether you use single or double quotes, the command itself contains both, so you have to worry about how to do the escaping. The
'\''sequence is how you embed a single quote into a single quoted string; the first single quote ends the current single quoted string; the backslash single quote adds a single quote, and the last single quote restarts a single string. The sequence'\'at the end could also be written'\''', but the last two quotes are a zero-length single quoted string, so I dropped them.This produces the nice output with tabs; you can verify by running:
The
echo $(eval $COMMAND)completely undoes all the careful spacing, flattening the entire list of files and times into a single line with each ‘word’ separated from the next by spaces.You can work around that issue by forcing
echoto respect the spacing (and newlines) with:but this raises the question of ‘why not use just
eval $COMMAND?’(Testing: bash 3.2.48 Mac OS X 10.7.4.)