• Uncategorized

About bash : How-do-I-set-a-variable-to-the-output-of-a-command-in-Bash

Question Detail

I have a pretty simple script that is something like the following:

#!/bin/bash

VAR1=”$1″
MOREF=’sudo run command against $VAR1 | grep name | cut -c7-‘

echo $MOREF

When I run this script from the command line and pass it the arguments, I am not getting any output. However, when I run the commands contained within the $MOREF variable, I am able to get output.

How can one take the results of a command that needs to be run within a script, save it to a variable, and then output that variable on the screen?

Question Answer

In addition to backticks `command`, command substitution can be done with $(command) or “$(command)”, which I find easier to read, and allows for nesting.

OUTPUT=$(ls -1)
echo “${OUTPUT}”

MULTILINE=$(ls \
-1)
echo “${MULTILINE}”

Quoting (“) does matter to preserve multi-line variable values; it is optional on the right-hand side of an assignment, as word splitting is not performed, so OUTPUT=$(ls -1) would work fine.
……………………………………………………
$(sudo run command)

If you’re going to use an apostrophe, you need `, not ‘. This character is called “backticks” (or “grave accent”):
#!/bin/bash

VAR1=”$1″
VAR2=”$2″

MOREF=`sudo run command against “$VAR1” | grep name | cut -c7-`

echo “$MOREF”

……………………………………………………
Some Bash tricks I use to set variables from commands
Sorry, there is a loong answer, but as bash is a shell, where the main goal is to run other unix commands and react on result code and/or output, ( commands are often piped filter, etc… ).
Storing command output in variables is something basic and fundamental.
Therefore, depending on

compatibility (posix)
kind of output (filter(s))
number of variable to set (split or interpret)
execution time (monitoring)
error trapping
repeatability of request (see long running background process, further)
interactivity (considering user input while reading from another input file descriptor)
do I miss something?

First simple, old (obsolet), and compatible way
myPi=`echo ‘4*a(1)’ | bc -l`
echo $myPi
3.14159265358979323844

Compatible, second way
As nesting could become heavy, parenthesis was implemented for this
myPi=$(bc -l <<<'4*a(1)') Using backticks in script is to be avoided today. Nested sample: SysStarted=$(date -d "$(ps ho lstart 1)" +%s) echo $SysStarted 1480656334 bash features Reading more than one variable (with Bashisms) df -k / Filesystem 1K-blocks Used Available Use% Mounted on /dev/dm-0 999320 529020 401488 57% / If I just want a used value: array=($(df -k /)) you could see an array variable: declare -p array declare -a array='([0]="Filesystem" [1]="1K-blocks" [2]="Used" [3]="Available" [ 4]="Use%" [5]="Mounted" [6]="on" [7]="/dev/dm-0" [8]="999320" [9]="529020" [10]= "401488" [11]="57%" [12]="/")' Then: echo ${array[9]} 529020 But I often use this: { read -r _;read -r filesystem size using avail prct mountpoint ; } < <(df -k /) echo $using 529020 ( The first read _ will just drop header line. ) Here, in only one command, you will populate 6 different variables (shown by alphabetical order): declare -p avail filesystem mountpoint prct size using declare -- avail="401488" declare -- filesystem="/dev/dm-0" declare -- mountpoint="/" declare -- prct="57%" declare -- size="999320" declare -- using="529020" Or { read -a head;varnames=(${head[@]//[K1% -]});varnames=(${head[@]//[K1% -]}); read ${varnames[@],,} ; } < <(LANG=C df -k /) Then: declare -p varnames ${varnames[@],,} declare -a varnames=([0]="Filesystem" [1]="blocks" [2]="Used" [3]="Available" [4]="Use" [5]="Mounted" [6]="on") declare -- filesystem="/dev/dm-0" declare -- blocks="999320" declare -- used="529020" declare -- available="401488" declare -- use="57%" declare -- mounted="/" declare -- on="" Or even: { read _ ; read filesystem dsk[{6,2,9}] prct mountpoint ; } < <(df -k /) declare -p mountpoint dsk declare -- mountpoint="/" declare -a dsk=([2]="529020" [6]="999320" [9]="401488") (Note Used and Blocks is switched there: read ... dsk[6] dsk[2] dsk[9] ...) ... will work with associative arrays too: read _ disk[total] disk[used] ... Dedicated fd using unnamed fifo: There is an elegent way! In this sample, I will read /etc/passwd file: users=() while IFS=: read -u $list user pass uid gid name home bin ;do ((uid>=500)) &&
printf -v users[uid] “%11d %7d %-20s %s\n” $uid $gid $user $home
done {list}&${COPROC[1]}
read -u $COPROC answer
echo $answer
12

echo >&${COPROC[1]} ‘pi=4*a(1)’
ray=42.0
printf >&${COPROC[1]} ‘2*pi*%s\n’ $ray
read -u $COPROC answer
echo $answer
263.89378290154263202896

printf >&${COPROC[1]} ‘pi*%s^2\n’ $ray
read -u $COPROC answer
echo $answer
5541.76944093239527260816

As bc is ready, running in background and I/O are ready too, there is no delay, nothing to load, open, close, before or after operation. Only the operation himself! This become a lot quicker than having to fork to bc for each operation!
Border effect: While bc stay running, they will hold all registers, so some variables or functions could be defined at initialisation step, as first write to ${COPROC[1]}, just after starting the task (via coproc).
Into a function newConnector
You may found my newConnector function on GitHub.Com or on my own site (Note on GitHub: there are two files on my site. Function and demo are bundled into one unique file which could be sourced for use or just run for demo.)
Sample:
source shell_connector.sh

tty
/dev/pts/20

ps –tty pts/20 fw
PID TTY STAT TIME COMMAND
29019 pts/20 Ss 0:00 bash
30745 pts/20 R+ 0:00 \_ ps –tty pts/20 fw

newConnector /usr/bin/bc “-l” ‘3*4’ 12

ps –tty pts/20 fw
PID TTY STAT TIME COMMAND
29019 pts/20 Ss 0:00 bash
30944 pts/20 S 0:00 \_ /usr/bin/bc -l
30952 pts/20 R+ 0:00 \_ ps –tty pts/20 fw

declare -p PI
bash: declare: PI: not found

myBc ‘4*a(1)’ PI
declare -p PI
declare — PI=”3.14159265358979323844″

The function myBc lets you use the background task with simple syntax.
Then for date:
newConnector /bin/date ‘-f – +%s’ @0 0
myDate ‘2000-01-01’
946681200
myDate “$(ps ho lstart 1)” boottime
myDate now now
read utm idl &-”
eval “exec $DATEIN>&-”
ps –tty pts/20 fw
PID TTY STAT TIME COMMAND
4936 pts/20 Ss 0:00 bash
5256 pts/20 S 0:00 \_ /usr/bin/bc -l
6358 pts/20 R+ 0:00 \_ ps –tty pts/20 fw

which is not needed, because all fd close when the main process finishes.
……………………………………………………
As they have already indicated to you, you should use `backticks`.
The alternative proposed $(command) works as well, and it also easier to read, but note that it is valid only with Bash or KornShell (and shells derived from those),
so if your scripts have to be really portable on various Unix systems, you should prefer the old backticks notation.
……………………………………………………
I know three ways to do it:

Functions are suitable for such tasks:**

func (){
ls -l
}

Invoke it by saying func.
Also another suitable solution could be eval:

var=”ls -l”
eval $var

The third one is using variables directly:

var=$(ls -l)

OR

var=`ls -l`

You can get the output of the third solution in a good way:

echo “$var”

And also in a nasty way:

echo $var

……………………………………………………
Just to be different:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)

……………………………………………………
When setting a variable make sure you have no spaces before and/or after the = sign. I literally spent an hour trying to figure this out, trying all kinds of solutions! This is not cool.
Correct:
WTFF=`echo “stuff”`
echo “Example: $WTFF”

Will Fail with error “stuff: not found” or similar
WTFF= `echo “stuff”`
echo “Example: $WTFF”

……………………………………………………
If you want to do it with multiline/multiple command/s then you can do this:

output=$( bash <&1)

……………………………………………………
This is another way and is good to use with some text editors that are unable to correctly highlight every intricate code you create:

read -r -d ” str < <(cat somefile.txt) echo "${#str}" echo "$str" ............................................................ You can use backticks (also known as accent graves) or $(). Like: OUTPUT=$(x+2); OUTPUT=`x+2`; Both have the same effect. But OUTPUT=$(x+2) is more readable and the latest one. ............................................................ Here are two more ways: Please keep in mind that space is very important in Bash. So, if you want your command to run, use as is without introducing any more spaces. The following assigns harshil to L and then prints it L=$"harshil" echo "$L" The following assigns the output of the command tr to L2. tr is being operated on another variable, L1. L2=$(echo "$L1" | tr [:upper:] [:lower:]) ............................................................ Some may find this useful. Integer values in variable substitution, where the trick is using $(()) double brackets: N=3 M=3 COUNT=$N-1 ARR[0]=3 ARR[1]=2 ARR[2]=4 ARR[3]=1 while (( COUNT < ${#ARR[@]} )) do ARR[$COUNT]=$((ARR[COUNT]*M)) (( COUNT=$COUNT+$N )) done

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.