what is the meaning of FOOBAR=foobar followed by a shell command? - shell

I see in shell scripts a variable assignment followed (without a semicolon) by a command. What is the meaning of that? It does not seem to affect this command, and it does not seem to affect the next command the way normal assignment would:
>echo $FOOBAR
>FOOBAR=1 echo $FOOBAR
>echo $FOOBAR
>
So, what does it do?

It sets that environment variable to that value only for that process.
Here, step by step, is what happens:
Original command: FOOBAR=1 echo $FOOBAR
Shell performs substitution: command is now FOOBAR=1 echo
Shell fork(2)s a new process
Environment variable is created in the new process: command is now echo (with $FOOBAR equal to 1)
New process is exec(3)ed: \n is output
New process exits and is reaped by the shell
At no point does the parent process see the assigned value of $FOOBAR.

On first run of echo $FOOBAR you will get empty result on your screen as the FOOBAR variable is not set yet.
Then when you set it to 1,and run it on the second time it will display 1.
Note: This value will be lost when you close your terminal where you wrote these. $ notation in shell and shell scripting in Unix like environments simply denotes a variable.
What happens in your case in second command i.e FOOBAR=1 echo $FOOBAR is that the line is not syntactically incorrect but it breaches the contract of equalto(=) which actually does not accept a space thus no error is thrown and FOOBAR=1 is just treated as a parameter and it fails while the command for echo executes successfully.
I would suggest try this :
FOOBAR=1
echo $FOOBAR
in different lines or
FOOBAR=1 ; echo $FOOBAR in one line.

Related

What is ${command_} in a shell script

I have googled and found nothing. I am reading a shell script file and there there is this line
echo "docker run command"
echo ${command_}
What does this mean? Please notice that
there is an underscore after "command"
command_ seems not to be defined elsewhere
What is ${command_} in a shell script
${command_} means to expand the variable named command_ to it's value.
What does this mean?
echo "docker run command" - means to execute the command echo (possibly a builtin) with one argument docker run command.
echo ${command_} - means to execute the command echo (possibly a builtin) with the result of expansion of the variable command_ will undergo word splitting expansion and then the result will be passed as arguments to the echo command.
there is an underscore after "command"
It doesn't matter - underscore is nothing special.
command_ seems not to be defined elsewhere
That means that ${command_} expansion will expand to an empty string, and the second echo will run with no arguments.
echo command is used to print the arguments passed. But if argument has $ to it, it means it evaluates its value and prints the same.
Here, you shell script would be having a variable named "command_". It'll just find its value, if defined, and print it. If its not defined, nothing will be printed.

Unexpected behavior of the unix echo command

while writing some script i found the following issue ,
suppose
set x = "param[xyz]";
echo $x
the output was echo:No match
But when i do ,
echo "$x"
i got the output: param[xyz]
so echo is doing a two way substitution ,
Initially echo $x was converted to echo param[xyz] and then it tried to look for the param[xyz] value .
But Ideally it should have just printed the variable whatever provided to it .
Does this behavior is a valid use case?
echo does no substitution at all, it's the shell that does it. It depends on the shell you use, but it seems that you are using a shell of the family of c-shells. Shell expands variables in the command line, so the first step is to generate:
[csh] echo param[xyz]
and then the shell does file match expansion, but as there is no file that correspond to the pattern the shell answers that there is no match. The message is somehow misleading as the shell reminds you what "command" was concerned not that the command failed by itself.
In the second try, enclosing the variable inside " prevent the shell to do other expansion and the shell launch the command with the argument obtained after the first expansion.
There exists another prevention if you use ', the shell won't ever expand variables:
[csh] echo '$x'
$x
Please refer to shell documentation and especially about expansion.
Another experience to convince you is to try with an non existing command:
[csh] weirdo z*
weirdo: No match
which is different than an non existing command:
[csh] weirdo
weirdo: Command not found.
If you'd use other shell behavior would be different:
[bash] echo z*
z*
because that shell produces as the argument the string itself in the case file matching is not working.
With:
[zsh] echo z*
zsh: no matches found: z*
the behavior is much more similar to c-shells but the message is much more clear, the shell failed at matching.

Echo-ing an environment variable returns string literal rather than environment variable value

I have two bash scripts. The first listens to a pipe "myfifo" for input and executes the input as a command:
fifo_name="myfifo"
[ -p $fifo_name ] || mkfifo $fifo_name;
while true
do
if read line; then
$line
fi
done <"$fifo_name"
The second passes a command 'echo $SET_VAR' to the "myfifo" pipe:
command='echo $SET_VAR'
command_to_pass="echo $command"
$command_to_pass > myfifo
As you can see, I want to pass 'echo $SET_VAR' through the pipe. In the listener process, I've set a $SET_VAR environment variable. I expect the output of the command 'echo $SET_VAR' to be 'var_value,' which is the value of the environment variable SET_VAR.
Running the first (the listener) script in one bash process and then passing a command via the second in another process gives the following result:
$SET_VAR
I expected to "var_value" to be printed. Instead, the string literal $SET_VAR is printed. Why is this the case?
Before I get to the problem you're reporting, I have to point out that your loop won't work. The while true part (without a break somewhere in the loop) will run forever. It'll read the first line from the file, loop, try to read a second line (which fails), loop again, try to read a third line (also fails), loop again, try to read a fourth line, etc... You want the loop to exit as soon as the read command fails, so use this:
while read line
do
# something I'll get to
done <"$fifo_name"
The other problem you're having is that the shell expands variables (i.e. replaces $var with the value of the variable var) partway through the process of parsing a command line, and when it's done that it doesn't go back and re-do the earlier parsing steps. In particular, if the variable's value included something like $SET_VAR it doesn't go back and expand that, since it's just finished the bit where it expands variables. In fact, the only thing it does with the expanded value is split it into "words" (based on whitespace), and expand any filename wildcards it finds -- no variable expansions happen, no quote or escape interpretation, etc.
One possible solution is to tell the shell to run the parsing process twice, with the eval command:
while read line
do
eval "$line"
done <"$fifo_name"
(Note that I used double-quotes around "$line" -- this prevents the word splitting and wildcard expansion I mentioned from happening before eval goes through the normal parsing process. If you think of your original code half-parsing the command in $line, without double-quotes it gets one and a half-parsed, which is weird. Double-quotes suppress that half-parsing stage, so the contents of the variable get parsed exactly once.)
However, this solution comes with a big warning, because eval has a well-deserved reputation as a bug magnet. eval makes it easy to do complex things without quite understanding what's going on, which means you tend to get scripts that work great in testing, then fail incomprehensibly later. And in my experience, when eval looks like the best solution, it probably means you're trying to solve the wrong problem.
So, what're you actually trying to do? If you're just trying to execute the lines coming from the fifo as shell commands, then you can use bash "$fifo_name" to run them in a subshell, or source "$fifo_name" to run them in the current shell.
BTW, the script that feeds the fifo:
command='echo $SET_VAR'
command_to_pass="echo $command"
$command_to_pass > myfifo
Is also a disaster waiting to happen. Putting commands in variables doesn't work very well in the shell (I second chepner's recommendation of BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!), and putting a command to print another command in a variable is just begging for trouble.
bash, by it's nature, reads commands from stdin. You can simply run:
bash < myfifo

How do I redirect output when the command to execute is stored in a variable in a bash script?

Consider the following script:
#!/bin/bash
CMD="echo hello world > /tmp/hello.out"
${CMD}
The output for this is:
hello world > /tmp/hello.out
How can I modify CMD so that the output gets redirected to hello.out?
For my use case, it is not feasible to either do this:
${CMD} > /tmp/hello.out
or to add this at the top of the script:
exec > /tmp/hello.out
No, there is no way to make a redirection happen from a variable.
Why?
The first thing the shell does with a command line is:
Each line that the shell reads from the standard input or a script is called a pipeline; it contains one or more commands separated by zero or
more pipe characters (|). For each pipeline it reads, the shell breaks it up into commands, sets up the I/O for the pipeline, then does the following for each command (Figure 7-1):
From: Learning the bash Shell Unix Shell Programming . Chapter Preview / Figure . Pdf
That means that even before starting with the first word of a command line, the redirections are set up.
The "Parameter Expansion" happens quite a lot latter (in step 6 of the Figure).
There is no way to set up redirections after a variable is expanded.
Unless ...
The "command line is reprocessed" using eval.
eval "$CMD"
But this comes with a lot of danger.
The command line is changed by the first processing in the 12 steps detailed in the book (quotes are removed, variables expanded, words split, etc.).
It is usually quite difficult to estimate all the changes and consequences before the line is actually processed.
And then, it is processed again.
You can use eval to instruct the shell to reinterpret the variable content as a shell command:
eval $CMD

Why 'test=6 echo $test' command doesnot work in bash?

I came across this neat command to check the time of any timezone in bash
TZ=US/Hawaii date
Which does same as
export TZ=US/Hawaii
date
How does the first command work, while the following command doesn't work?
test=6 echo $test
Variables are expanded before executing the commands in a line. So in the last example, $test is expanded before the assignment is done.
You can get the effect you want with:
test=6 bash -c 'echo $test'
This executes a new shell process. Since the argument is quoted, variables aren't expanded by the original shell, the subshell expands it in the environment where $test is set.
In the first, the call to date uses the value of TZ that is put in its environment. In the second, the expansion of $test is performed by the shell before echo is actually run. The value of test in echo's environment is irrelevant. A near-equivalent that works would be
(test=6; echo $test)

Resources