Home How to set a variable to the output from a command in Bash?
Reply: 13

How to set a variable to the output from a command in Bash?

John
1#
John Published in 2011-01-10 20:58:02Z

I am working on a simple scripting project for work that involves the use of Bash. I have a pretty simple script that is something like the following:

#!/bin/bash

VAR1="$1"
VAR2="$2"

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. I would like to know how one can 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?

Andy Lester
2#
Andy Lester Reply to 2014-10-17 13:50:35Z

In addition to the backticks, you can use $(), which I find easier to read, and allows for nesting.

OUTPUT="$(ls -1)"
echo "${OUTPUT}"

Quoting (") does matter to preserve multi-line values.

htoip
3#
htoip Reply to 2013-02-04 19:30:15Z

You're using the wrong kind of apostrophe. You need `, not '. This character is called "backticks" (or "grave accent").

Like this:

#!/bin/bash

VAR1="$1"
VAR2="$2"

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

echo $MOREF
DigitalRoss
4#
DigitalRoss Reply to 2011-01-10 21:07:40Z

Just to be different:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
bitwelder
5#
bitwelder Reply to 2011-01-11 22:14:19Z

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 korn shells (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.

BoltClock
6#
BoltClock Reply to 2014-05-17 16:22:55Z

I know three ways to do:

1) Functions are suitable for such tasks:

func (){
ls -l
}

Invoke it by saying func

2) Also another suitable solution could be eval:

var="ls -l"
eval $var

3) The third one is using variables directly:

var=$(ls -l)
OR
var=`ls -l`

you can get output of third solution in good way:

echo "$var"

and also in nasty way:

echo $var
Aquarius Power
7#
Aquarius Power Reply to 2015-05-10 21:44:04Z

This is another way, 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"
Jahid
8#
Jahid Reply to 2015-12-29 11:16:00Z

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

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

Or:

output=$(
#multiline/multiple command/s
)

Example:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Output:

first
second
third

Using heredoc you can simplify things pretty easily by breaking down your long single line code into multiline one. Another example:

output="$( ssh -p $port $user@$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"
Gus
9#
Gus Reply to 2015-11-22 11:59:46Z

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
Pratik Patil
10#
Pratik Patil Reply to 2016-02-09 08:03:47Z

You can use back-ticks(also known as accent graves) or $(). Like as-

OUTPUT=$(x+2);
OUTPUT=`x+2`;

Both have the same effect. But OUTPUT=$(x+2) is more readable and the latest one.

Harshil
11#
Harshil Reply to 2016-06-22 10:09:23Z

Here are two more ways: Please keep in mind space is very important in bash so if you want your command to run use as is without introducing anymore spaces.

  1. following assigns harshil to L and then prints it

    L=$"harshil"
    echo "$L"
    
  2. following assigns output of a command tr to L2. tr is being operated on another variable L1.

    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    
F. Hauri
12#
F. Hauri Reply to 2018-02-14 17:27:14Z

Some bash tricks I use to set variables from commands

2nd Edit 2018-02-12: Adding a special way, see at very bottom of this!

2018-01-25 Edit: add sample function (for populating vars about disk usage)

First simple old and compatible way

myPi=`echo '4*a(1)' | bc -l`
echo $myPi 
3.14159265358979323844

Mostly compatible, second way

As nesting could become heavy, parenthesis was implemented for this

myPi=$(bc -l <<<'4*a(1)')

Nested sample:

SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted 
1480656334

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 Used value:

array=($(df -k /))

you could see 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 prefer this:

{ read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /)
echo $used
529020

1st read foo will just skip header line (variable $foo will contain something like Filesystem 1K-blocks Used Available Use% Mounted on)

Sample function for populating some variables:

#!/bin/bash

declare free=0 total=0 used=0

getDiskStat() {
    local foo
    {
        read foo
        read foo total used free foo
    } < <(
        df -k ${1:-/}
    )
}

getDiskStat $1
echo $total $used $free

Nota: declare line is not required, just for readability.

About sudo cmd | grep ... | cut ...

shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash

(Please avoid useless cat! So this is just 1 fork less:

shell=$(grep $USER </etc/passwd | cut -d : -f 7)

All pipes (|) implies forks. Where another process have to be run, accessing disk, libraries calls and so on.

So using sed for sample, will limit subprocess to only one fork:

shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell

And with bashisms:

But for many actions, mostly on small files, bash could do the job himself:

while IFS=: read -a line ; do
    [ "$line" = "$USER" ] && shell=${line[6]}
  done </etc/passwd
echo $shell
/bin/bash

or

while IFS=: read loginname encpass uid gid fullname home shell;do
    [ "$loginname" = "$USER" ] && break
  done </etc/passwd
echo $shell $loginname ...
Going further about variable splitting...

Have a look at my answer to How do I split a string on a delimiter in Bash?

Alternative: reducing forks by using background long-running tasks

2nd Edit 2018-02-12

In order to prevent multiple forks like

myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")

or

myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)

And because date and bc could work line by line:

bc -l <<<$'3*4\n5*6'
12
30

date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288

We could use long running background process to make jobs repetitively, without having to initiate new fork for each request:

mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc

(of course, FD 5 and 6 have to be unused!)... From there, you could use this process by:

echo "3*4" >&5
read -u 6 foo
echo $foo
12

echo >&5 "pi=4*a(1)"
echo >&5 "2*pi*12"
read -u 6 foo
echo $foo
75.39822368615503772256

Into a function newConnector

You may found my newConnector function on GitHub.Com or on my own site (Nota on github, there is two files, on my site, function and demo are bundled into 1 file wich could be sourced for use or just run for demo)

Sample:

. 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 let you use the background task with simple syntax, and 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 </proc/uptime
myBc "$now-$boottime" uptime
printf "%s\n" ${utm%%.*} $uptime
  42134906
  42134906

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
  32615 pts/20   S      0:00  \_ /bin/date -f - +%s
   3162 pts/20   R+     0:00  \_ ps --tty pts/20 fw

From there, if you want to end one of background process, you just have to close his fd:

eval "exec $DATEOUT>&-"
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

wich is not needed, because all fd close when main process finish.

Emil
13#
Emil Reply to 2017-07-18 11:42:01Z

When setting a variable make sure you have NO Spaces before and/or after the = sign. Literally spent an hour trying to figure this, 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"
Diego Velez
14#
Diego Velez Reply to 2017-12-28 23:44:48Z

You need to use either

$(command-here)

or

`command-here`

example

#!/bin/bash

VAR1="$1"
VAR2="$2"

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

echo $MOREF
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.823564 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO