Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store the output of command in a variable without creating a subshell [Bash <v4]

Tags:

ksh has a really interesting construct to do this, detailed in this answer: https://stackoverflow.com/a/11172617/636849

Since Bash 4.0, there is a builtin mapfile builtin command that should solve this problem: http://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html

But strangely, it doesn't seem to work with process substitution:

foo () { echo ${BASH_SUBSHELL}; }
mapfile -t foo_output <(foo) # FAIL: hang forever here
subshell_depth=${foo_output[0]} # should be 0

But how to do this in Bash v3.2 ?

like image 797
Lucas Cimon Avatar asked Feb 07 '14 15:02

Lucas Cimon


People also ask

How do you store output of a command to a variable?

To store the output of a command in a variable, you can use the shell command substitution feature in the forms below: variable_name=$(command) variable_name=$(command [option ...] arg1 arg2 ...) OR variable_name='command' variable_name='command [option ...]

What is the command used to make variable available to Subshells?

To make a variable available in a subshell (or any other subprogram of the shell), we have to “export” the variable with the export command.

How do you make a variable unchangeable in shell?

The shell provides a way to protect variables as read-only by using the readonly command. After a variable is marked read-only, we cannot change the value of the variable. As we can see from the output, we cannot assign to the variable declared as readonly. If we drop "$", we get an error.


2 Answers

Here's another way to do it, which is different enough that it warrants a separate answer. I think this method is subshell-free and bash sub-process free:

ubuntu@ubuntu:~$ bar () { echo "$BASH_SUBSHELL $BASHPID"; }
ubuntu@ubuntu:~$ bar
0 8215
ubuntu@ubuntu:~$ mkfifo /tmp/myfifo
ubuntu@ubuntu:~$ exec 3<> /tmp/myfifo
ubuntu@ubuntu:~$ unlink /tmp/myfifo
ubuntu@ubuntu:~$ bar 1>&3
ubuntu@ubuntu:~$ read -u3 a
ubuntu@ubuntu:~$ echo $a
0 8215
ubuntu@ubuntu:~$ exec 3>&-
ubuntu@ubuntu:~$

The trick here is to use exec to open the FIFO in read-write mode with an FD, which seems to have the side-effect of making the FIFO non-blocking. Then you can redirect your command to the FD without it blocking, then read the FD.

Note that the FIFO will be a limited-size buffer, probably around 4K, so if your command produces more output than this, it will end up blocking again.

like image 157
Digital Trauma Avatar answered Sep 20 '22 01:09

Digital Trauma


This question comes up very often while looking how to just capture output of any "printing" command into variable. So for anyone looking it's possible (since bash v3.1.0) with:

printf -v VARIABLE_NAME "whatever you need here: %s" $ID

If you tweak your scripts for speed then you can use pattern of setting some global variable at the end of functions instead of just "echoing" it - use this with care, it's sometimes criticized as leading to hard to maintain code.

like image 39
yatsek Avatar answered Sep 22 '22 01:09

yatsek