我有一个非常简单的脚本,如下所示:
#!/bin/bash VAR1="$1" MOREF='sudo run command against $VAR1 | grep name | cut -c7-' echo $MOREF
当我从命令行运行此脚本并将参数传递给它时,我没有得到任何输出 . 但是,当我运行 $MOREF 变量中包含的命令时,我能够获得输出 .
$MOREF
我想知道如何获取需要在脚本中运行的命令的结果,将其保存到变量,然后在屏幕上输出该变量?
你需要使用其中之一
$(command-here)
要么
`command-here`
例
#!/bin/bash VAR1="$1" VAR2="$2" MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)" echo "$MOREF"
您可以使用反蜱(也称为重点坟墓)或 $() . 像 - 作为 -
$()
OUTPUT=$(x+2); OUTPUT=`x+2`;
两者都有相同的效果 . 但OUTPUT = $(x 2)更具可读性和最新可读性 .
正如他们已经向你指出的那样,你应该使用'反引号' .
提议的替代方案 $(command) 也可以工作,并且它也更容易阅读,但请注意它仅对bash或korn shell(以及从这些shell派生的shell)有效,所以如果你的脚本必须在各种Unix系统上真正可移植,那么你应该更喜欢旧的反引号表示法 .
$(command)
我知道三种方法:
1) Functions are suitable for such tasks:
func (){ ls -l }
通过说 func 来调用它
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`
你可以很好地得到第三种解决方案的输出:
echo "$var"
还有令人讨厌的方式:
echo $var
有些人可能觉得这很有用 . 变量替换中的整数值,其中诀窍是使用 $(()) 双括号:
$(())
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
Update (2018):正确的方法是
$(sudo run command)
你使用的是错误的撇号 . 你需要 ``` ,而不是 ' . 此字符称为"backticks"(或"grave accent") .
'
像这样:
#!/bin/bash VAR1="$1" VAR2="$2" MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-` echo "$MOREF"
除了反引号( command )之外,你可以使用 $(command) ,我觉得它更容易阅读,并允许嵌套 .
command
OUTPUT="$(ls -1)" echo "${OUTPUT}"
引用( " )对保留多行值很重要 .
"
2nd Edit 2018-02-12: Adding a special way, see at very bottom of this!
2018-01-25 Edit: add sample function (用于填充有关磁盘使用情况的变量)
myPi=`echo '4*a(1)' | bc -l` echo $myPi 3.14159265358979323844
由于嵌套可能变得很重,因此实现了括号
myPi=$(bc -l <<<'4*a(1)')
嵌套样本:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s) echo $SysStarted 1480656334
df -k / Filesystem 1K-blocks Used Available Use% Mounted on /dev/dm-0 999320 529020 401488 57% /
如果我只想要使用值:
array=($(df -k /))
你可以看到数组变量:
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]="/")'
然后:
echo ${array[9]} 529020
但我更喜欢这个:
{ read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /) echo $used 529020
1st read foo 将跳过 Headers 行(变量 $foo 将包含类似 Filesystem 1K-blocks Used Available Use% Mounted on 的内容)
read foo
$foo
Filesystem 1K-blocks Used Available Use% Mounted on
#!/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 行不是必需的,只是为了便于阅读 .
declare
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7) echo $shell /bin/bash
(请避免无用的 cat !所以这只是1叉少:
cat
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
所有管道( | )都表示叉子 . 必须运行另一个进程,访问磁盘,库调用等等 .
|
所以使用 sed 作为示例,将子进程限制为只有一个fork:
sed
shell=$(sed </etc/passwd "s/^$USER:.*://p;d") echo $shell
但对于许多操作,主要是小文件,bash可以自己完成这项工作:
while IFS=: read -a line ; do [ "$line" = "$USER" ] && shell=${line[6]} done </etc/passwd echo $shell /bin/bash
while IFS=: read loginname encpass uid gid fullname home shell;do [ "$loginname" = "$USER" ] && break done </etc/passwd echo $shell $loginname ...
看看我对How do I split a string on a delimiter in Bash?的回答
2nd Edit 2018-02-12: 为了防止像多个叉子
myPi=$(bc -l <<<'4*a(1)' myRay=12 myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")
myStarted=$(date -d "$(ps ho lstart 1)" +%s) mySessStart=$(date -d "$(ps ho lstart $$)" +%s)
因为 date 和 bc 可以逐行工作:
date
bc
bc -l <<<$'3*4\n5*6' 12 30 date -f - +%s < <(ps ho lstart 1 $$) 1516030449 1517853288
我们可以使用长时间运行的后台进程重复创建作业,而不必为每个请求启动新的fork:
mkfifo /tmp/myFifoForBc exec 5> >(bc -l >/tmp/myFifoForBc) exec 6</tmp/myFifoForBc rm /tmp/myFifoForBc
(当然,FD 5 和 6 必须不使用!)......从那里,您可以通过以下方式使用此过程:
5
6
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或my own site上找到我的 newConnector 函数(在github上有Nota,在我的网站上有两个文件,函数和演示被捆绑到1个文件中,可以使用或仅运行演示)
newConnector
样品:
. 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"
函数 myBc 允许您使用简单语法的后台任务,并使用日期:
myBc
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
从那里,如果你想结束一个后台进程,你只需要关闭他的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
不需要,因为所有fd在主要过程结束时关闭 .
设置变量时,请确保 = 符号之前和/或之后有 NO Spaces . 花了一个小时试图弄清楚这一点,尝试各种解决方案!这不酷 .
正确:
WTFF=`echo "stuff"` echo "Example: $WTFF"
将失败并出现错误:(东西:未找到或类似)
WTFF= `echo "stuff"` echo "Example: $WTFF"
只是为了与众不同:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
还有两种方法:
请记住bash中的空间非常重要 . 因此,如果您希望命令运行,请按原样使用,而不再引入任何空格 .
L=$"harshil" echo "$L"
tr
L2=$(echo "$L1" | tr [:upper:] [:lower:])
如果你想用multiline / multiple命令执行它,那么你可以这样做:
output=$( bash <<EOF #multiline/multiple command/s EOF )
要么:
output=$( #multiline/multiple command/s )
例:
#!/bin/bash output="$( bash <<EOF echo first echo second echo third EOF )" echo "$output"
输出:
first second third
使用heredoc,您可以通过将长单行代码分解为多行代码来轻松简化操作 . 另一个例子:
output="$( ssh -p $port $user@$domain <<EOF #breakdown your long ssh command into multiline here. EOF )"
这是另一种方式,适用于某些文本编辑器,这些编辑器无法正确突出显示您创建的每个复杂代码 .
read -r -d '' str < <(cat somefile.txt) echo "${#str}" echo "$str"
13 回答
你需要使用其中之一
$(command-here)
要么
例
您可以使用反蜱(也称为重点坟墓)或
$()
. 像 - 作为 -两者都有相同的效果 . 但OUTPUT = $(x 2)更具可读性和最新可读性 .
正如他们已经向你指出的那样,你应该使用'反引号' .
提议的替代方案
$(command)
也可以工作,并且它也更容易阅读,但请注意它仅对bash或korn shell(以及从这些shell派生的shell)有效,所以如果你的脚本必须在各种Unix系统上真正可移植,那么你应该更喜欢旧的反引号表示法 .我知道三种方法:
1) Functions are suitable for such tasks:
通过说
func
来调用它2) Also another suitable solution could be eval:
3) The third one is using variables directly:
你可以很好地得到第三种解决方案的输出:
还有令人讨厌的方式:
有些人可能觉得这很有用 . 变量替换中的整数值,其中诀窍是使用
$(())
双括号:Update (2018):正确的方法是
你使用的是错误的撇号 . 你需要 ``` ,而不是
'
. 此字符称为"backticks"(或"grave accent") .像这样:
除了反引号(
command
)之外,你可以使用$(command)
,我觉得它更容易阅读,并允许嵌套 .引用(
"
)对保留多行值很重要 .我用来从命令设置变量的一些bash技巧
2nd Edit 2018-02-12: Adding a special way, see at very bottom of this!
2018-01-25 Edit: add sample function (用于填充有关磁盘使用情况的变量)
第一个简单的旧兼容方式
大多数兼容,第二种方式
由于嵌套可能变得很重,因此实现了括号
嵌套样本:
读取多个变量(用bashisms)
如果我只想要使用值:
你可以看到数组变量:
然后:
但我更喜欢这个:
1st
read foo
将跳过 Headers 行(变量$foo
将包含类似Filesystem 1K-blocks Used Available Use% Mounted on
的内容)用于填充一些变量的示例函数:
Nota:
declare
行不是必需的,只是为了便于阅读 .关于sudo cmd | grep ... |切...
(请避免无用的
cat
!所以这只是1叉少:所有管道(
|
)都表示叉子 . 必须运行另一个进程,访问磁盘,库调用等等 .所以使用
sed
作为示例,将子进程限制为只有一个fork:和bashisms:
但对于许多操作,主要是小文件,bash可以自己完成这项工作:
要么
关于变量分裂的进一步说明......
看看我对How do I split a string on a delimiter in Bash?的回答
备选:使用后台长时间运行的任务减少分叉
2nd Edit 2018-02-12: 为了防止像多个叉子
要么
因为
date
和bc
可以逐行工作:我们可以使用长时间运行的后台进程重复创建作业,而不必为每个请求启动新的fork:
(当然,FD
5
和6
必须不使用!)......从那里,您可以通过以下方式使用此过程:进入函数newConnector
您可以在GitHub.Com或my own site上找到我的
newConnector
函数(在github上有Nota,在我的网站上有两个文件,函数和演示被捆绑到1个文件中,可以使用或仅运行演示)样品:
函数
myBc
允许您使用简单语法的后台任务,并使用日期:从那里,如果你想结束一个后台进程,你只需要关闭他的fd:
不需要,因为所有fd在主要过程结束时关闭 .
设置变量时,请确保 = 符号之前和/或之后有 NO Spaces . 花了一个小时试图弄清楚这一点,尝试各种解决方案!这不酷 .
正确:
将失败并出现错误:(东西:未找到或类似)
只是为了与众不同:
还有两种方法:
请记住bash中的空间非常重要 . 因此,如果您希望命令运行,请按原样使用,而不再引入任何空格 .
tr
的输出分配给L2 .tr
正在另一个变量L1上运行 .如果你想用multiline / multiple命令执行它,那么你可以这样做:
要么:
例:
输出:
使用heredoc,您可以通过将长单行代码分解为多行代码来轻松简化操作 . 另一个例子:
这是另一种方式,适用于某些文本编辑器,这些编辑器无法正确突出显示您创建的每个复杂代码 .