How do I set a variable to the output of a command in Bash?
我有一个非常简单的脚本,如下所示:
1 2 3 4 5 6 | #!/bin/bash VAR1="$1" MOREF='sudo run command against $VAR1 | grep name | cut -c7-' echo $MOREF |
当我从命令行运行这个脚本并将参数传递给它时,我没有得到任何输出。但是,当我运行
如何获取需要在脚本中运行的命令的结果,将其保存到变量中,然后在屏幕上输出该变量?
除了倒勾(
1 2 3 4 5 6 | OUTPUT="$(ls -1)" echo"${OUTPUT}" MULTILINE=$(ls \\ -1) echo"${MULTILINE}" |
引用(
正确的方法是
1 | $(sudo run command) |
如果要使用撇号,则需要
这样地:
1 2 3 4 5 6 7 8 | #!/bin/bash VAR1="$1" VAR2="$2" MOREF=`sudo run command against"$VAR1" | grep name | cut -c7-` echo"$MOREF" |
正如他们已经向你指出的,你应该使用"倒计时"。
提出的替代方案
我用一些bash技巧从命令中设置变量
第二次编辑2018-02-12:添加不同的方式,在底部搜索长期运行的任务!
2018-01-25编辑:添加示例函数(用于填充关于磁盘使用的变量)
第一种简单、古老、兼容的方式1 2 3 | myPi=`echo '4*a(1)' | bc -l` echo $myPi 3.14159265358979323844 |
大部分兼容,第二种方式
由于嵌套可能变得很重,因此为此实现了括号
1 | myPi=$(bc -l <<<'4*a(1)') |
嵌套样本:
1 2 3 | SysStarted=$(date -d"$(ps ho lstart 1)" +%s) echo $SysStarted 1480656334 |
读取多个变量(使用bashims)
1 2 3 | df -k / Filesystem 1K-blocks Used Available Use% Mounted on /dev/dm-0 999320 529020 401488 57% / |
如果我只想要使用的值:
1 | array=($(df -k /)) |
您可以看到数组变量:
1 2 3 4 | 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]="/")' |
然后:
1 2 | echo ${array[9]} 529020 |
但我更喜欢这样:
1 2 3 | { read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /) echo $used 529020 |
1st
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/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 |
注:不需要
1 2 3 | shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7) echo $shell /bin/bash |
(请避免无用的
1 | shell=$(grep $USER </etc/passwd | cut -d : -f 7) |
所有管道(
因此,使用
1 2 | shell=$(sed </etc/passwd"s/^$USER:.*://p;d") echo $shell |
对于bashims:
但是对于许多操作,主要是小文件,bash可以自己完成:
1 2 3 4 5 | while IFS=: read -a line ; do ["$line" ="$USER" ] && shell=${line[6]} done </etc/passwd echo $shell /bin/bash |
或
1 2 3 4 | while IFS=: read loginname encpass uid gid fullname home shell;do ["$loginname" ="$USER" ] && break done </etc/passwd echo $shell $loginname ... |
进一步了解变量拆分…
请看一下我对如何在bash中分隔符上拆分字符串的答案。
备选方案:通过使用后台长时间运行任务减少分叉第二次编辑:2018-02-12:为了防止多个叉子
1 2 3 | myPi=$(bc -l <<<'4*a(1)' myRay=12 myCirc=$(bc -l <<<" 2 * $myPi * $myRay") |
或
1 2 | myStarted=$(date -d"$(ps ho lstart 1)" +%s) mySessStart=$(date -d"$(ps ho lstart $$)" +%s) |
这很好用,但多用叉子又重又慢。
像
见:
1 2 3 4 5 6 7 8 | bc -l <<<$'3*4\ 5*6' 12 30 date -f - +%s < <(ps ho lstart 1 $$) 1516030449 1517853288 |
因此,我们可以使用长时间运行的后台进程来完成许多作业,而不必为每个请求启动一个新的分叉。
我们只需要一些文件描述符和FIFOS就可以正确地做到这一点:
1 2 3 4 | mkfifo /tmp/myFifoForBc exec 5> >(bc -l >/tmp/myFifoForBc) exec 6</tmp/myFifoForBc rm /tmp/myFifoForBc |
(当然,fd
1 2 3 4 5 6 7 8 9 10 | 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 |
变成一个函数
您可以在github.com或我自己的网站上找到我的
Sample:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | . 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" |
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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\ " ${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 |
从这里开始,如果你想结束一个后台进程,你只需要关闭他的fd:
1 2 3 4 5 6 7 | 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 |
这是不需要的,因为当主进程完成时,所有FD都将关闭。
我知道三种方法:
1)功能适用于此类任务:
1 2 3 | func (){ ls -l } |
调用它的方法是说
2)另一个合适的解决方案是Eval:
1 2 | var="ls -l" eval $var |
3)第三种是直接使用变量:
1 2 3 | var=$(ls -l) OR var=`ls -l` |
您可以很好地获得第三个解决方案的输出:
1 | echo"$var" |
也有一些不好的地方:
1 | echo $var |
只是为了与众不同:
1 | MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-) |
设置变量时,确保在=符号之前和/或之后没有空格。字面上花了一个小时试图解决这个问题,尝试各种解决方案!这不酷。
对的:
1 2 | WTFF=`echo"stuff"` echo"Example: $WTFF" |
将因错误而失败:(资料:未找到或类似)
1 2 | WTFF= `echo"stuff"` echo"Example: $WTFF" |
如果要使用多行/多命令/s执行此操作,则可以执行以下操作:
1 2 3 4 | output=$( bash <<EOF #multiline/multiple command/s EOF ) |
或:
1 2 3 | output=$( #multiline/multiple command/s ) |
例子:
1 2 3 4 5 6 7 8 | #!/bin/bash output="$( bash <<EOF echo first echo second echo third EOF )" echo"$output" |
输出:
1 2 3 | first second third |
使用HereDoc,您可以很容易地将长的单行代码分解为多行代码,从而简化工作。另一个例子:
1 2 3 4 | output="$( ssh -p $port $user@$domain <<EOF #breakdown your long ssh command into multiline here. EOF )" |
你也需要使用
或
1 | `command-here` |
例子
1 2 3 4 5 6 7 8 | #!/bin/bash VAR1="$1" VAR2="$2" MOREF="$(sudo run command against"$VAR1" | grep name | cut -c7-)" echo"$MOREF" |
你可以使用反勾号(也被称为重音标记)或
1 2 | OUTPUT=$(x+2); OUTPUT=`x+2`; |
两者都有相同的效果。但output=$(x+2)更可读,是最新的。
这是另一种方法,很好地用于一些无法正确突出显示所创建的每一个复杂代码的文本编辑器。
1 2 3 | read -r -d '' str < <(cat somefile.txt) echo"${#str}" echo"$str" |
还有两种方法:
请记住,空间在bash中非常重要。因此,如果希望命令运行,请按原样使用,而不引入任何空间。
下面将harshil分配给l,然后打印它
1 2 | L=$"harshil" echo"$L" |
下面将命令
1 | L2=$(echo"$L1" | tr [:upper:] [:lower:]) |
有些人可能会觉得这很有用。变量替换中的整数值,其中技巧是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |