bash - value - shell script assign command output to variable
How to set a variable to the output of a command in Bash? (9)
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.
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.
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?
Update (2018): the right way is
$(sudo run command)
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"
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.
following assigns harshil to L and then prints it
L=$"harshil" echo "$L"
following assigns the output of the command
tr
to L2.tr
is being operated on another variable L1.L2=$(echo "$L1" | tr [:upper:] [:lower:])
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
In addition to backticks (`command`
), you can use $(command)
, which I find easier to read, and allows for nesting.
OUTPUT="$(ls -1)"
echo "${OUTPUT}"
Quoting ("
) does matter to preserve multi-line values.
Just to be different:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
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"
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"
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"