In:
$(echo "
export TEST_A=1
export TEST_B=2
")
being $(...)
, just one expansion is parsed as a simple command.
So that $(...)
expansion undergoes the split+glob operator. The output of echo
is trimmed of its trailing newline characters and split into several words on IFS characters (newline, space and tab by default):
export
TEST_A=1
export
TEST_B=2
And each word undergoes globbing. Here they expand to themselves since they don't contain wildcard characters. So after globbing, we still have the same list of four strings.
The command to run is derived from the first one. Here export
and all those strings are passed as arguments to that command. So, here, it's as if you had written:
export TEST_A=1 export TEST_B=2
So you're exporting the TEST_A
variable with value 1, the export
variable without changing its value and the TEST_B
variable with value 2.
In the last one, you're running:
unset TEST_A export TEST_B=1
If you want the output of echo
to be treated as shell code to interpret, that's where you want to use eval
and not use the split+glob operator (quote your command substitution):
eval "$(echo "
unset TEST_A
export TEST_B=1
")"
That one is also a simple command. Two strings: eval
and <newline><spaces>unset TEST_A<newline><spaces>export TEST_B=1
.
eval
evaluates the content of its arguments as shell code. So it runs those two shell command lines: unset TEST_A
and export TEST_B=1
(again, two simple commands).
Here, if lsusb
drops an error, the error message is not redirected, only errors from grep
. Errors from lsusb
still go the the usual stderr
, i.e. the terminal (or whatever stderr
was when the script started.
VENDOR=$(lsusb -D "$DEVICE" | grep idVendor 2>&1)
E.g.
$ blah=$(ls -l /nonexisting | grep foo 2>&1)
ls: cannot access '/nonexisting': No such file or directory
$ echo "blah: '$blah'"
blah: ''
You'll need to redirect both of their outputs as a group to have the command substitution capture the error, too:
$ blah=$( { ls -l /nonexisting | grep foo; } 2>&1)
$ echo "blah: '$blah'"
blah: 'ls: cannot access '/nonexisting': No such file or directory'
Using separate redirections like $( foo 2>&1 | grep 2>&1 )
would send the errors of the first command to the pipe, and for grep
to filter, which in the above example would again produce an empty output.
Best Answer
After the substition happens (which BTW in POSIX could only target the left side before any ">") there is no more evaluation on whether there is any ">" so the approach you envisioned wouldn't work.
If you don't care about POSIX-conformity (after all you tagged this as 'bash') you could still find a solution by dynamically setting the right side but I would personally go for a totally different approach; have a look at the following post detailing a verbose/silent mode based on custom file descriptors: https://stackoverflow.com/a/20942015/2261442.
A code excerpt from that post to show how nice it would then look like: