1924

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?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
John
  • 19,653
  • 5
  • 20
  • 24
  • 1
    A related question http://stackoverflow.com/questions/25116521/how-do-i-get-a-part-of-the-output-of-a-command-in-linux-bash – Sandeepan Nath Aug 25 '16 at 07:09
  • 52
    As an aside, all-caps variables are [defined by POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html) for variable names with meaning to the operating system or shell itself, whereas names with at least one lowercase character are reserved for application use. Thus, consider using lowercase names for your own shell variables to avoid unintended conflicts (keeping in mind that setting a shell variable will overwrite any like-named environment variable). – Charles Duffy Mar 27 '17 at 15:56
  • 2
    As an aside, capturing output into a variable just so you can then `echo` the variable is a [useless use of `echo`,](http://www.iki.fi/era/unix/award.html#echo) and a useless use of variables. – tripleee Jul 21 '18 at 06:58
  • 2
    As a further aside, storing output in variables is often unnecessary. For small, short strings you will need to reference multiple times in your program, this is completely fine, and exactly the way to go; but for processing any nontrivial amounts of data, you want to reshape your process into a pipeline, or use a temporary file. – tripleee Jan 18 '19 at 07:56
  • Variation: *"I know how to use `variable=$(command)` but I think `"$string"` is a valid `command`";* https://stackoverflow.com/questions/37194795/assign-output-of-shell-command-to-variable – tripleee Oct 08 '20 at 06:42
  • @tripleee: ... I would suspect that he is NOT trying to store the output in a variable simply so he can echo it back out. I'm basing this off the fact that maybe not everyone always wants to share their code verbatim, and also/or want to keep it more focused on the core issue so they can get help AND help more people in the future. ... ALSO, there is the first line where they straight up said that they "had a pretty simple script that is SOMETHING like the following:" So there's that ;-) I guess at the same time your links sparked my curiosity so they ARE still appreciated. – Brent Rittenhouse Oct 25 '20 at 00:58
  • The title ought to be more specific, as this question covers the case of ***(variable) input to the external command*** (in a (Bash) variable." (not just a constant string for the external command). – Peter Mortensen Nov 10 '20 at 01:13

14 Answers14

2688

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.

vstepaniuk
  • 388
  • 3
  • 8
Andy Lester
  • 81,480
  • 12
  • 93
  • 144
  • 64
    Can we provide some separator for multi line output ? – Aryan Feb 21 '13 at 12:26
  • 22
    White space (or lack of whitespace) matters – Ali Apr 24 '14 at 10:40
  • 2
    so `echo "${OUTPUT}"` preserves line breaks, whereas `echo $OUTPUT` doesn't? – timhc22 Apr 09 '15 at 16:09
  • 8
    @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. – Charles Duffy Apr 21 '15 at 15:37
  • 4
    Ah thanks! So is there any benefit to the curly braces? – timhc22 Apr 21 '15 at 16:01
  • 16
    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}` – rich remer Jun 01 '16 at 23:16
  • 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 – Neal Jun 12 '16 at 05:30
  • @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' :)"` – Volker Siegel Aug 16 '16 at 10:20
  • 3
    Also: no spaces are permitted between OUTPUT, =, and the quote. This tripped me up. – Michael Currie Sep 03 '16 at 13:39
  • How it would be the case where there is argument passing, like this call `OUTPUT=$(./myscript "$@")` (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` ? – nightcod3r Oct 31 '16 at 21:45
  • is it possible to explain why the command works the way you suggested it? – Charlie Parker Dec 17 '16 at 02:53
  • @nightcod3r, eh? `output=$(./myscript "$@")` works perfectly well. – Charles Duffy Mar 27 '17 at 15:58
  • @CharlesDuffy, @nightcod3r, the difference between your two code examples is that Charles has added a space after `myscript`. – Luke Griffiths Dec 15 '18 at 23:01
  • Taking it down to the bare minimum: a=$(ls -l) && echo "$a" The quotes around the $a is the key to preserving newlines. – Vic K Feb 05 '20 at 15:47
309
$(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"
Rob Bednark
  • 19,968
  • 18
  • 67
  • 100
Ilya Kogan
  • 20,368
  • 15
  • 78
  • 134
  • 35
    The backtick syntax is obsolescent, and you really need to put double quotes around the variable interpolation in the `echo`. – tripleee Dec 28 '15 at 12:28
  • 12
    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 – zbstof Jan 05 '16 at 11:07
  • 4
    tripleeee's comment is correct. In cygwin (May 2016), `` doesn't work while `$()` works. Couldn't fix until I saw this page. – toddwz May 13 '16 at 12:42
  • 2
    Elaboration such as an example on **Update (2018)** would be appreciated. – Eduard Jul 13 '18 at 13:31
129

Some Bash tricks I use to set variables from commands

Sorry, there is a loong answer, but as is a , where the main goal is to run other commands and react to resut code and/or output, ( commands are often piped filter, etc... ).

Storing command output in variables is something basic and fundamental.

Therefore, depending on

  • compatibility ()
  • 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, 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

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 foo ; read filesystem size using avail prct mountpoint ; } < <(df -k /)
echo $using
529020

The first read foo will just skip header line, but in only one command, you will populate 7 different variables:

declare -p avail filesystem foo mountpoint prct size using
declare -- avail="401488"
declare -- filesystem="/dev/dm-0"
declare -- foo="Filesystem     1K-blocks   Used Available Use% Mounted on"
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 foo ; 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 foo disk[total] disk[used] ...

Dedicated fd using unnamed fifo:

There is an elegent way:

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}</etc/passwd

Using this way (... read -u $list; ... {list}<inputfile) leave STDIN free for other purposes, like user interaction.

Then

echo -n "${users[@]}"
       1000    1000 user         /home/user
...
      65534   65534 nobody       /nonexistent

and

echo ${!users[@]}
1000 ... 65534

echo -n "${users[1000]}"
      1000    1000 user       /home/user

This could be used with static files or even /dev/tcp/xx.xx.xx.xx/yyy with x for ip address or hostname and y for port number:

{
    read -u $list -a head          # read header in array `head`
    varnames=(${head[@]//[K1% -]}) # drop illegal chars for variable names
    while read -u $list ${varnames[@],,} ;do
        ((pct=available*100/(available+used),pct<10)) &&
            printf "WARN: FS: %-20s on %-14s %3d <10 (Total: %11u, Use: %7s)\n" \
                "${filesystem#*/mapper/}" "$mounted" $pct $blocks "$use"
     done
 } {list}< <(LANG=C df -k)

And of course with inline documents:

while IFS=\; read -u $list -a myvar ;do
    echo ${myvar[2]}
done {list}<<"eof"
foo;bar;baz
alice;bob;charlie
$cherry;$strawberry;$memberberries
eof

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 one 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 itself:

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

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 a long running background process to make many jobs, without having to initiate a new fork for each request.

Under , there is a built-in function: coproc:

coproc bc -l
echo 4*3 >&${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 uniq 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 </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 processes, you just have to close its 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 the main process finishes.

F. Hauri
  • 51,421
  • 13
  • 88
  • 109
  • 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. – Capricorn1 Aug 02 '17 at 18:02
  • 2
    @capricorn1 That's a [useless use of `echo`](http://www.iki.fi/era/unix/award.html#echo); you want simply `grep "$CONTAINER_NAME"` – tripleee Nov 15 '17 at 04:20
  • See this [sample using `tput` as *background task* for color rendering on terminal](https://unix.stackexchange.com/a/521120/27653)!! – F. Hauri Jan 12 '20 at 16:24
  • I probably miss something here: `kubectl get ns | while read -r line; do echo `$line | grep Term | cut -d' ' -f1`; done` prints out for each `$line` an empty line and then `bash: xxxx: command not found`. However I would expect that it prints out just `xxx` – papanito Feb 14 '20 at 16:04
  • 1
    Instead of all the "Edits" notes and strikeovers (that is what the revision history is for), it would be better to have it as if this answer was written today. If there are some lessons to be learned it could be documented in a section, e.g. *"Things not to do"*. – Peter Mortensen Nov 10 '20 at 00:26
77

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
bitwelder
  • 1,027
  • 8
  • 10
  • 25
    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 *cough*HP-UX*cough* which are stuck firmly in the early nineties.) – tripleee Sep 18 '14 at 14:40
  • 27
    Incorrect. `$()` is fully compatible with POSIX sh, as standardized over two decades ago. – Charles Duffy Apr 21 '15 at 15:38
  • 3
    Note that `/bin/sh` on Solaris 10 still does not recognize `$(…)` — and AFAIK that's true on Solaris 11 too. – Jonathan Leffler Dec 18 '15 at 20:07
  • 3
    @JonathanLeffler It is actually no more the case with Solaris 11 where `/bin/sh` is `ksh93`. – jlliagre Dec 21 '16 at 17:05
  • 2
    @tripleee - response three years late :-) but I've used `$()` in the POSIX shell on HP-UX for the past 10+ years. – Bob Jarvis - Reinstate Monica Nov 15 '17 at 03:14
  • 1
    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. – tripleee Nov 15 '17 at 04:28
  • 1
    just wanted to add that $(`command`) will interpret the commands output as a command – Fuseteam Mar 13 '19 at 18:49
53

I know three ways to do it:

  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 the output of the third solution in a good way:

echo "$var"

And also in a nasty way:

echo $var
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
MLSC
  • 5,350
  • 6
  • 41
  • 85
  • 1
    The first two do not seem to answer the question as it currently stands, and the second is commonly held to be dubious. – tripleee Sep 22 '16 at 04:39
  • 1
    As someone who is entirely new to bash, why is `"$var"` good and `$var` nasty? – Peter Jan 25 '18 at 07:36
  • @Peter https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable – tripleee Jul 21 '18 at 07:10
32

Just to be different:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
DigitalRoss
  • 135,013
  • 23
  • 230
  • 316
24

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"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Emil
  • 469
  • 4
  • 7
  • 3
    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. – Charles Duffy Dec 15 '18 at 23:05
  • 1
    Yes, a Bash gotcha. – Peter Mortensen Nov 18 '19 at 14:18
15

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 a multiline one. Another example:

output="$( ssh -p $port $user@$domain <<EOF
# Breakdown your long ssh command into multiline here.
EOF
)"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jahid
  • 18,228
  • 8
  • 79
  • 95
  • 2
    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; ...)` – tripleee Dec 28 '15 at 12:27
  • Then similarly `'bash -c "bash -c \"bash -c ...\""'` would be "different", too; but I don't see the point of that. – tripleee Dec 29 '15 at 08:59
  • @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) – Jahid Dec 29 '15 at 09:05
  • 1
    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. – tripleee Dec 29 '15 at 09:08
  • @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. – Jahid Dec 29 '15 at 09:12
  • But then you'd wrap that in `sudo bash -c '...'` in which the `bash` is useful precisely for that reason. – tripleee Dec 29 '15 at 09:25
  • @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](http://stackoverflow.com/a/31272222/3744681). – Jahid Dec 29 '15 at 10:01
  • Then don't. You still have not explained how `variable=$(bash << – tripleee Dec 29 '15 at 10:03
  • @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 < – Jahid Dec 29 '15 at 11:04
  • 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: http://ideone.com/VbVinB – tripleee Dec 29 '15 at 11:05
  • @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. – Jahid Dec 29 '15 at 11:20
  • 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 Dec 29 '15 at 11:21
  • See e.g. http://stackoverflow.com/a/17338083/874188 although many of the other answers (including the accepted answer) inexplicably prefer here documents. – tripleee Dec 29 '15 at 11:23
  • @tripleee yap, using a string would do too. But I lik the heredoc better. It feels simpler than putting commands in string. – Jahid Dec 29 '15 at 11:27
  • 2
    When I use heredoc with ssh, I precise the command to run `ssh -p $port $user@$domain /bin/bash < – F. Hauri Dec 20 '16 at 07:40
10

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"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Diego Velez
  • 735
  • 7
  • 14
  • 3
    `$()` is much better than backticks. See: [What is the benefit of using $() instead of backticks in shell scripts?](https://stackoverflow.com/q/9449778/6862601) – codeforester Apr 09 '18 at 18:56
  • 1
    I didn't know you could nest but it makes perfect sense, thank you very much for the info! – Diego Velez Apr 11 '18 at 02:58
6

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"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Aquarius Power
  • 3,212
  • 5
  • 26
  • 56
6

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Pratik Patil
  • 2,976
  • 2
  • 29
  • 28
  • 2
    Parenthesis was implemented in order to permit nesting. – F. Hauri Dec 20 '16 at 07:37
  • 1
    `x+2` is not a valid command, most places. To the extent that this isn't misleading beginners to think this is how you do arithmetic, this duplicates existing answers. – tripleee May 27 '21 at 15:56
6

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)
cafebabe1991
  • 4,416
  • 1
  • 25
  • 36
5

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.

  1. The following assigns harshil to L and then prints it

    L=$"harshil"
    echo "$L"
    
  2. 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:])
    
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Harshil
  • 995
  • 13
  • 20
  • 4
    1. [`$"..."` probably doesn't do what you think it does](https://www.gnu.org/software/bash/manual/bashref.html#Locale-Translation). 2. This is already given in Andy Lester's answer. – gniourf_gniourf Jun 22 '16 at 10:35
  • @gniourf_gniourf is right: see [bash localization won't work with multilines](http://stackoverflow.com/q/14027030/1765658). But under [tag:bash], you could use `echo ${L1,,}` to downcase, or `echo ${L1^^}` to upcase. – F. Hauri Dec 20 '16 at 07:34
4

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
Gus
  • 4,483
  • 4
  • 26
  • 29
  • 1
    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. – tripleee Dec 28 '15 at 12:22
  • 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. – Gus Dec 28 '15 at 13:38
  • 1
    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! – F. Hauri Dec 20 '16 at 07:25
  • @F.Hauri... bash is getting more & more like perl the deeper you go into it! – roblogic Nov 07 '17 at 23:22