Something I've come across a few times recently: how to set a variable and execute commands using that variable in a one-liner? I often want to do this when I have one string which needs to be embedded in a long list of commands in multiple places:
The most obvious approach is this (contrived example):
FOO=foo date > $FOO.txt && echo "Wrote to $FOO.txt"
The problem is that $FOO gets expanded with the value of whatever FOO is when the command is parsed, not when it's executed. So if FOO is initially empty, that example will write to .txt
and not foo.txt
.
One solution is to use a subshell:
(FOO=foo; date > $FOO.txt && echo "Wrote to $FOO.txt")
That works fine. FOO
is assigned inside the subshell and evaluated correctly where it's used.
What I don't get is why this doesn't work:
FOO=foo (date > $FOO.txt && echo "Wrote to $FOO.txt")
That's a bash: syntax error near unexpected token ``('
error.
How come that doesn't temporarily assign FOO=foo
and then execute the subshell?
(
,((
,[[
,if
,while
, etc. Anything that does not begin with one of that small set of strings is a simple command. If compound commands could start with precommand assignments, you would need an arbitrary amount of lookahead to decide what kind of command you were parsing.FOO=foo bash -c 'date > $FOO.txt && echo "Wrote to $FOO.txt"'
, which is equivalent of a subshell.FOO=foo date > $FOO.txt
does not write the current date tofoo.txt
, because$FOO.txt
undergoes parameter expansion before the assignmentFOO=foo
takes effect.