By John


2011-01-10 20:58:02 8 Comments

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?

14 comments

@cafebabe1991 2019-06-24 10:35:54

If the command that you are trying to execute fails, it would write the output onto the error stream and would then be printed out to the console. To avoid it, you must redirect the error stream

result=$(ls -l something_that_does_not_exist 2>&1)

@Andy Lester 2011-01-10 21:04:18

In addition to backticks `command` you can use $(command) or "$(command)" which I find easier to read, and allow for nesting.

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

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

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

@Aryan 2013-02-21 12:26:06

Can we provide some separator for multi line output ?

@David Doria 2014-01-24 18:35:21

@Ali 2014-04-24 10:40:31

White space (or lack of whitespace) matters

@timhc22 2015-04-09 16:09:21

so echo "${OUTPUT}" preserves line breaks, whereas echo $OUTPUT doesn't?

@Charles Duffy 2015-04-21 15:37:42

@timhc22, the curly braces are irrelevant; it's only the quotes that are important re: whether expansion results are string-split and glob-expanded before being passed to the echo command.

@timhc22 2015-04-21 16:01:21

Ah thanks! So is there any benefit to the curly braces?

@rich remer 2016-06-01 23:16:41

Curly braces can be used when the variable is immediately followed by more characters which could be interpreted as part of the variable name. e.g. ${OUTPUT}foo. They are also required when performing inline string operations on the variable, such as ${OUTPUT/foo/bar}

@Neal 2016-06-12 05:30:25

ok, what do you do if you need to quote something inside of the quoted command expansion? I have tried using 's and they don't work

@Volker Siegel 2016-08-16 10:20:07

@Aryan Multi line output is just output containing newline characters; They can be converted to something else, but that would be handled independently. Try OUTPUT="$(seq 3)" versus OUTPUT="$(seq 3 | tr '\n' :)"

@Michael Currie 2016-09-03 13:39:02

Also: no spaces are permitted between OUTPUT, =, and the quote. This tripped me up.

@nightcod3r 2016-10-31 21:45:13

How it would be the case where there is argument passing, like this call OUTPUT=$(./myscript "[email protected]") (this actually does not work at all). How to keep the structure of the arguments when the original script is run, for example, with ./script.sh "hello world" rest ?

@Charlie Parker 2016-12-17 02:53:03

is it possible to explain why the command works the way you suggested it?

@Charles Duffy 2017-03-27 15:58:44

@nightcod3r, eh? output=$(./myscript "[email protected]") works perfectly well.

@Luke Griffiths 2018-12-15 23:01:27

@CharlesDuffy, @nightcod3r, the difference between your two code examples is that Charles has added a space after myscript.

@bitwelder 2011-01-11 22:14:19

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.

@tripleee 2014-09-18 14:40:12

They are overtly cautious. Backticks have been deprecated by POSIX a long time ago; the more modern syntax should be available in most shells from this millennium. (There are still legacy environments coughHP-UXcough which are stuck firmly in the early nineties.)

@Charles Duffy 2015-04-21 15:38:35

Incorrect. $() is fully compatible with POSIX sh, as standardized over two decades ago.

@Jonathan Leffler 2015-12-18 20:07:04

Note that /bin/sh on Solaris 10 still does not recognize $(…) — and AFAIK that's true on Solaris 11 too.

@jlliagre 2016-12-21 17:05:30

@JonathanLeffler It is actually no more the case with Solaris 11 where /bin/sh is ksh93.

@Bob Jarvis 2017-11-15 03:14:04

@tripleee - response three years late :-) but I've used $() in the POSIX shell on HP-UX for the past 10+ years.

@tripleee 2017-11-15 04:28:22

Good to hear! A number of vendor-specific Unices (HP, AIX, SunOS/Solaris) used to be notorious for having extremely quirky userspace utilities with legacy behaviors which prevented any attempt at writing portable code; if HP (and Solaris!) are now finally out of there, the world is a better place.

@Fuseteam 2019-03-13 18:49:14

just wanted to add that $(command) will interpret the commands output as a command

@Ilya Kogan 2011-01-10 21:00:27

The right way is

$(sudo run command)

If you're going to use an 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"

@tripleee 2015-12-28 12:28:19

The backtick syntax is obsolescent, and you really need to put double quotes around the variable interpolation in the echo.

@Zotov 2016-01-05 11:07:35

I would add that you have to be careful with the spaces around '=' in the assignment above. You shouln't have any spaces there, otherwise you'll get an incorrect assignment

@toddwz 2016-05-13 12:42:21

tripleeee's comment is correct. In cygwin (May 2016), `` doesn't work while $() works. Couldn't fix until I saw this page.

@Eduard 2018-07-13 13:31:42

Elaboration such as an example on Update (2018) would be appreciated.

@F. Hauri 2016-12-20 07:06:50

Some tricks I use to set variables from commands

2nd Edit 2018-02-12: Adding a different way, search at bottom of this for long-running tasks!

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, 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 backgrounded 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)

This work fine, but running many forks is heavy and slow.

and commands like date and bc could make many operations, line by line!!

See:

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

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

So we could use long running background process to make many jobs, without having to initiate a new fork for each request.

We just need some file descriptors and fifos for doing this properly:

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 which 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

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

@Capricorn1 2017-08-02 18:02:42

The nested sample above is what I was looking for. There may be a simpler way, but what I was looking for was the way to find out if a docker container already exists given its name in an environment variable. So for me: EXISTING_CONTAINER=$(docker ps -a | grep "$(echo $CONTAINER_NAME)") was the statement I was looking for.

@tripleee 2017-11-15 04:20:20

@capricorn1 That's a useless use of echo; you want simply grep "$CONTAINER_NAME"

@F. Hauri 2018-01-25 09:41:05

2018 Edit: Add sample function (for populating vars about disk usage)

@Harshil 2016-06-22 10:09:23

Here are two more ways:

Please keep in mind that space is very important in . So, if you want your command to run, use as is without introducing any more spaces.

  1. following assigns harshil to L and then prints it

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

    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    

@gniourf_gniourf 2016-06-22 10:35:52

1. $"..." probably doesn't do what you think it does. 2. This is already given in Andy Lester's answer.

@F. Hauri 2016-12-20 07:34:10

@gniourf_gniourf is right: see bash localization won't work with multilines. But under bash, you could use echo ${L1,,} to downcase, or echo ${L1^^} to upcase.

@Diego Velez 2017-12-28 23:44:48

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"

@codeforester 2018-04-09 18:56:04

@Diego Velez 2018-04-11 02:58:36

I didn't know you could nest but it makes perfect sense, thank you very much for the info!

@Emil 2017-07-18 11:42:01

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"

@Charles Duffy 2018-12-15 23:05:15

The version with the space means something different: var=value somecommand runs somecommand with var in its environment having the value value. Thus, var= somecommand is exporting var in the environment of somecommand with an empty (zero-byte) value.

@Pratik Patil 2016-02-09 08:03:47

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.

@F. Hauri 2016-12-20 07:36:33

bash: x+2: command not found

@F. Hauri 2016-12-20 07:37:33

Parenthesis was implemented in order to permit nesting.

@Jahid 2015-06-01 17:38:32

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 [email protected]$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"

@tripleee 2015-12-28 12:27:00

What's with the second bash inside the command substitution? You are already creating a subshell by the command substitution itself. If you want to put multiple commands, just separate them by newline or semicolon. output=$(echo first; echo second; ...)

@Jahid 2015-12-29 08:43:42

@tripleee just different ways of getting it done.

@tripleee 2015-12-29 08:59:23

Then similarly 'bash -c "bash -c \"bash -c ...\""' would be "different", too; but I don't see the point of that.

@Jahid 2015-12-29 09:05:54

@tripleee heredoc means something more than that. You can do the same with some other commands like ssh sudo -s executing mysql commands inside, etc.. (instead of bash)

@tripleee 2015-12-29 09:08:29

I don't feel we are communicating properly. I am challenging the usefulness over variable=$(bash -c 'echo "foo"; echo "bar"') over variable=$(echo "foo"; echo "bar") -- the here document is just a quoting mechanism and doesn't really add anything except another useless complication.

@Jahid 2015-12-29 09:12:01

@tripleee that's right if you think only of those cases. Add other things like executing multiple sudo commands or ssh commands, I am sure you will see the differences. For example sudo commad;sudo command; isn't helpful at all.

@tripleee 2015-12-29 09:25:37

But then you'd wrap that in sudo bash -c '...' in which the bash is useful precisely for that reason.

@Jahid 2015-12-29 10:01:10

@tripleee heredoc is safer than bash -c. Not to mention all the different quotes it requires. It makes things very confusing and it's pretty easy to mess things up with bash -c. And I generally try to avoid using bash -c. It's not safe.

@tripleee 2015-12-29 10:03:48

Then don't. You still have not explained how variable=$(bash <<<"echo 'moo'; echo 'bar'") is useful compared to variable=$(echo 'moo'; echo 'bar') and I can't help but point out that the former, too, introduces various quoting complications.

@Jahid 2015-12-29 11:04:21

@tripleee the bash is just an example. The main thing is the use of heredoc. For example you can get the output of a long ssh command by dividing it into a multi-line command with heredoc var=$(ssh <<EOF...). It simplifies the command a lot compared to bash -c or putting all in the same line.

@tripleee 2015-12-29 11:05:44

Yes, but that's still apples and oranges. ssh adds something (a secure remote connection) while bash does not. The here document is not necessary for breaking the command over multiple lines. Random demo: ideone.com/VbVinB

@Jahid 2015-12-29 11:20:04

@tripleee that's apples and orranges, run a multiline ssh command with single login like that. Let's see how you do it.. I would be happy to know such a method easier than using heredoc.

@tripleee 2015-12-29 11:21:32

You can use a multiline string as the argument to ssh which has some benefits, such as not tying up standard input for reading commands. I continue to fail to see any positive relevance to the actual question.

@tripleee 2015-12-29 11:23:55

See e.g. stackoverflow.com/a/17338083/874188 although many of the other answers (including the accepted answer) inexplicably prefer here documents.

@Jahid 2015-12-29 11:27:58

@tripleee yap, using a string would do too. But I lik the heredoc better. It feels simpler than putting commands in string.

@F. Hauri 2016-12-20 07:40:52

When I use heredoc with ssh, I precise the command to run ssh -p $port [email protected]$domain /bin/bash <<EOF in order to prevent Pseudo-terminal will not be allocated because stdin is not a terminal. warning

@Gus 2015-11-22 11:59:46

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

@tripleee 2015-12-28 12:22:22

This does not seem to have any relevance for this question. It would be a reasonable answer if somebody were to ask how to multiply a number in an array by a constant factor, though I don't recall ever seeing anyone asking that (and then a for ((...)) loop would seem like a better match for the loop variable). Also, you should not use uppercase for your private variables.

@Gus 2015-12-28 13:38:18

I disagree with the "relevance" part. The question clearly reads: How to set a variable equal to the output from a command in Bash? And I added this answer as a complement because I got here looking for a solution which helped me with the code I later posted. Regarding the uppercase vars, thanks for that.

@tripleee 2015-12-28 15:30:31

So which command's output are you capturing here?

@F. Hauri 2016-12-20 07:25:55

This could be written ARR=(3 2 4 1);for((N=3,M=3,COUNT=N-1;COUNT < ${#ARR[@]};ARR[COUNT]*=M,COUNT+=N)){ :;} but I agree with @tripleee: I don't understand what do this, there!

@roblogic 2017-11-07 23:22:33

@F.Hauri... bash is getting more & more like perl the deeper you go into it!

@Aquarius Power 2015-05-10 21:44:04

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"

@codeforester 2018-04-24 02:01:22

This doesn't deal with OP's question, which is really about command substitution, not process substitution.

@Aquarius Power 2018-04-29 00:54:14

@codeforester but did this work for you too?

@MLSC 2014-02-13 07:31:00

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

@tripleee 2016-09-22 04:39:37

The first two do not seem to answer the question as it currently stands, and the second is commonly held to be dubious.

@Peter 2018-01-25 07:36:58

As someone who is entirely new to bash, why is "$var" good and $var nasty?

@tripleee 2018-07-21 07:10:55

@DigitalRoss 2011-01-10 21:07:40

Just to be different:

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

Related Questions

Sponsored Content

62 Answered Questions

[SOLVED] Calling an external command in Python

63 Answered Questions

[SOLVED] Get the source directory of a Bash script from within the script itself

  • 2008-09-12 20:39:56
  • Jiaaro
  • 1554959 View
  • 4614 Score
  • 63 Answer
  • Tags:   bash directory

12 Answered Questions

[SOLVED] Looping through the content of a file in Bash

  • 2009-10-05 17:52:54
  • Peter Mortensen
  • 1418396 View
  • 1217 Score
  • 12 Answer
  • Tags:   linux bash loops unix io

33 Answered Questions

[SOLVED] How do I split a string on a delimiter in Bash?

30 Answered Questions

[SOLVED] How to concatenate string variables in Bash

19 Answered Questions

[SOLVED] How do I tell if a regular file does not exist in Bash?

  • 2009-03-12 14:48:43
  • Bill the Lizard
  • 2392586 View
  • 3041 Score
  • 19 Answer
  • Tags:   bash file-io scripting

31 Answered Questions

[SOLVED] How do I parse command line arguments in Bash?

32 Answered Questions

[SOLVED] How to check if a variable is set in Bash?

  • 2010-08-30 14:54:38
  • prosseek
  • 1164068 View
  • 1385 Score
  • 32 Answer
  • Tags:   bash shell variables

11 Answered Questions

[SOLVED] How to copy a folder from remote to local using scp?

13 Answered Questions

[SOLVED] How to echo shell commands as they are executed

Sponsored Content