我有一个非常简单的脚本,如下所示:
#!/bin/bash VAR1="$1"MOREF='sudo run command against $VAR1 | grep name | cut -c7-' echo $MOREF
当我从命令行运行此脚本并将参数传递给它时,我没有得到任何输出。但是,当我运行$MOREF变量中包含的命令时,我能够获得输出。
$MOREF
如何获取需要在脚本中运行的命令的结果,将其保存到变量中,然后在屏幕上输出该变量?
$(sudo run command)
如果你要使用撇号,你需要`,而不是'。这个字符被称为“反引号”(或“严重口音”):
`
'
#!/bin/bash VAR1="$1"VAR2="$2" MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-` echo "$MOREF"
除了反引号`command`,命令替换可以用$(command)或"$(command)"完成,我发现这更容易阅读,并允许嵌套。
`command`
$(command)
"$(command)"
OUTPUT=$(ls -1)echo "${OUTPUT}" MULTILINE=$(ls \-1)echo "${MULTILINE}"
引用(")对保留多行变量值很重要;它在赋值的右侧是可选的,就像不执行单词拆分一样,所以OUTPUT=$(ls -1)可以正常工作。
"
OUTPUT=$(ls -1)
只是为了与众不同:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
正如他们已经向您表明的那样,您应该使用“反引号”。
提议的替代方案$(command)也有效,并且更易于阅读,但请注意,它仅适用于Bash或KornShell(以及从中派生的shell),因此,如果您的脚本必须在各种Unix系统上真正可移植,您应该更喜欢旧的反引号符号。
我知道有三种方法可以做到:
函数适用于这样的任务:**
func (){ls -l}
通过func来调用它。
func
另一个合适的解决方案可以是ava:
var="ls -l"eval $var
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"
而且以一种令人讨厌的方式:
echo $var
这是另一种方式,适用于一些无法正确突出显示您创建的每个复杂代码的文本编辑器:
read -r -d '' str < <(cat somefile.txt)echo "${#str}"echo "$str"
如果您想使用多行/多个命令/s执行此操作,那么您可以这样做:
output=$( bash <<EOF# Multiline/multiple command/sEOF)
或:
output=$(# Multiline/multiple command/s)
示例:
#!/bin/bashoutput="$( bash <<EOFecho firstecho secondecho thirdEOF)"echo "$output"
输出:
firstsecondthird
使用heldoc,您可以通过将长单行代码分解为多行代码来轻松简化事情。另一个例子:
output="$( ssh -p $port $user@$domain <<EOF# Breakdown your long ssh command into multiline here.EOF)"
有些人可能会觉得这很有用。变量替换中的整数值,技巧是使用$(())双括号:
$(())
N=3M=3COUNT=$N-1ARR[0]=3ARR[1]=2ARR[2]=4ARR[3]=1 while (( COUNT < ${#ARR[@]} ))doARR[$COUNT]=$((ARR[COUNT]*M))(( COUNT=$COUNT+$N ))done
您可以使用反引号(也称为重音坟墓)或$()。
$()
喜欢:
OUTPUT=$(x+2);OUTPUT=`x+2`;
两者都有相同的效果。但是OUTPUT=$(x+2)更具可读性,也是最新的。
这里还有两种方法:
请记住,空间在Bash中非常重要。因此,如果您希望您的命令运行,请按原样使用,而不会引入任何更多的空格。
下面将harshil分配给L,然后打印它
harshil
L
L=$"harshil"echo "$L"
The following assigns the output of the command tr to L2. tr is being operated on another variable, L1.
tr
L2=$(echo "$L1" | tr [:upper:] [:lower:])
对不起,有一个长期的答案,但bash是shell,其中主要目标是运行其他unix命令并对结果代码和/或输出做出反应,(命令通常是管道过滤器等)。
将命令输出存储在变量中是基本和基本的东西。
因此,取决于
myPi=`echo '4*a(1)' | bc -l`echo $myPi3.14159265358979323844
由于嵌套可能变得很重,为此实现了括号
myPi=$(bc -l <<<'4*a(1)')
今天要避免在脚本中使用反引号。
嵌套示例:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s)echo $SysStarted1480656334
df -k /Filesystem 1K-blocks Used Available Use% Mounted on/dev/dm-0 999320 529020 401488 57% /
如果我只想要一个使用值:
array=($(df -k /))
你可以看到一个阵列变量:
declare -p arraydeclare -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 -r _;read -r filesystem size using avail prct mountpoint ; } < <(df -k /)echo $using529020
(第一个read _将只是下降标题行。)在这里,在只有一个命令中,您将填充6个不同个变量(按字母顺序显示):
read _
declare -p avail filesystem mountpoint prct size usingdeclare -- avail="401488"declare -- filesystem="/dev/dm-0"declare -- mountpoint="/"declare -- prct="57%"declare -- size="999320"declare -- using="529020"
或
{ read -a head;varnames=(${head[@]//[K1% -]});varnames=(${head[@]//[K1% -]});read ${varnames[@],,} ; } < <(LANG=C df -k /)
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=""
甚至:
{ read _ ; read filesystem dsk[{6,2,9}] prct mountpoint ; } < <(df -k /)declare -p mountpoint dskdeclare -- mountpoint="/"declare -a dsk=([2]="529020" [6]="999320" [9]="401488")
(注Used和Blocks在那里切换:read ... dsk[6] dsk[2] dsk[9] ...)
Used
Blocks
read ... dsk[6] dsk[2] dsk[9] ...
…也将与关联数组一起工作:read _ disk[total] disk[used] ...
read _ disk[total] disk[used] ...
有一种优雅的方式!在这个示例中,我将读取/etc/passwd文件:
/etc/passwd
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 $homedone {list}</etc/passwd
使用这种方式(... read -u $list; ... {list}<inputfile)让#1可以用于其他目的,例如用户交互。
... read -u $list; ... {list}<inputfile
然后
echo -n "${users[@]}"1000 1000 user /home/user...65534 65534 nobody /nonexistent
和
echo ${!users[@]}1000 ... 65534 echo -n "${users[1000]}"1000 1000 user /home/user
这可以用于静态文件,甚至/dev/tcp/xx.xx.xx.xx/yyy,x用于ip地址或主机名,y用于端口号或命令的输出:
/dev/tcp/xx.xx.xx.xx/yyy
x
y
{read -u $list -a head # read header in array `head`varnames=(${head[@]//[K1% -]}) # drop illegal chars for variable nameswhile 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)
当然是内联文档:
while IFS=\; read -u $list -a myvar ;doecho ${myvar[2]}done {list}<<"eof"foo;bar;bazalice;bob;charlie$cherry;$strawberry;$memberberrieseof
由于这个答案足够长,对于这一段,我只会让你参考这个答案#0,我使用无名FIFO读取文件,使用语法如:
exec {FD}<"$file" # open unnamed fifo for readIFS=';' read -ru $FD -a headlinewhile IFS=';' read -ru $FD -a row ;do ...
…但使用bash可加载CSV模块。
在我的网站上,你可以找到相同的脚本,读取#0作为内联文档。
#!/bin/bash declare free=0 total=0 used=0 mpnt='??' getDiskStat() \{\{read _read _ total used free _ mpnt} < <(df -k ${1:-/})} getDiskStat $1echo "$mpnt: Tot:$total, used: $used, free: $free."
注意:declare行不是必需的,只是为了易读性。
declare
sudo cmd | grep ... | cut ...
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)echo $shell/bin/bash
(请避免无用的cat!所以这只是少了一个分叉:
cat
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
所有管道(|)意味着分叉。必须运行另一个进程、访问磁盘、库调用等。
|
因此,使用sed作为示例,将子进程限制为只有一个叉:
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/passwdecho $shell/bin/bash
while IFS=: read loginname encpass uid gid fullname home shell;do[ "$loginname" = "$USER" ] && breakdone </etc/passwdecho $shell $loginname ...
看看我的答案如何在Bash中的分隔符上拆分字符串?
为了防止多个分叉,如
myPi=$(bc -l <<<'4*a(1)'myRay=12myCirc=$(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'1230 date -f - +%s < <(ps ho lstart 1 $$)15160304491517853288
因此,我们可以使用长时间运行的后台进程来完成许多作业,而不必为每个请求启动一个新的叉。
你可以看看减少分叉如何使Mandelbrot bash,从超过8小时提高到不到5秒。
在bash下,有一个内置函数:coproc:
coproc
coproc bc -lecho 4*3 >&${COPROC[1]}read -u $COPROC answerecho $answer12 echo >&${COPROC[1]} 'pi=4*a(1)'ray=42.0printf >&${COPROC[1]} '2*pi*%s\n' $rayread -u $COPROC answerecho $answer263.89378290154263202896 printf >&${COPROC[1]} 'pi*%s^2\n' $rayread -u $COPROC answerecho $answer5541.76944093239527260816
由于bc已准备就绪,后台运行和I/O也已准备就绪,因此没有延迟,在操作之前或之后没有任何要加载、打开、关闭的内容。只有操作本身!这比必须为每个操作分叉到bc快得多!
边界效应:当bc保持运行时,它们将保存所有寄存器,因此可以在初始化步定义一些变量或函数,因为首先写入${COPROC[1]},就在启动任务之后(通过coproc)。
${COPROC[1]}
newConnector
您可能会在我自己的网站的GitHub. Com或上找到我的newConnector函数(在GitHub上注意:我的网站上有两个文件。函数和演示捆绑在一个唯一的文件中,可以来源以供使用或仅用于演示。)
样本:
source shell_connector.sh tty/dev/pts/20 ps --tty pts/20 fwPID TTY STAT TIME COMMAND29019 pts/20 Ss 0:00 bash30745 pts/20 R+ 0:00 \_ ps --tty pts/20 fw newConnector /usr/bin/bc "-l" '3*4' 12 ps --tty pts/20 fwPID TTY STAT TIME COMMAND29019 pts/20 Ss 0:00 bash30944 pts/20 S 0:00 \_ /usr/bin/bc -l30952 pts/20 R+ 0:00 \_ ps --tty pts/20 fw declare -p PIbash: declare: PI: not found myBc '4*a(1)' PIdeclare -p PIdeclare -- PI="3.14159265358979323844"
函数myBc允许您使用简单语法的后台任务。
myBc
然后是日期:
newConnector /bin/date '-f - +%s' @0 0myDate '2000-01-01'946681200myDate "$(ps ho lstart 1)" boottimemyDate now nowread utm idl </proc/uptimemyBc "$now-$boottime" uptimeprintf "%s\n" ${utm%%.*} $uptime4213490642134906 ps --tty pts/20 fwPID TTY STAT TIME COMMAND29019 pts/20 Ss 0:00 bash30944 pts/20 S 0:00 \_ /usr/bin/bc -l32615 pts/20 S 0:00 \_ /bin/date -f - +%s3162 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
从那里,如果你想结束一个后台进程,你只需要关闭它的#0:
eval "exec $DATEOUT>&-"eval "exec $DATEIN>&-"ps --tty pts/20 fwPID TTY STAT TIME COMMAND4936 pts/20 Ss 0:00 bash5256 pts/20 S 0:00 \_ /usr/bin/bc -l6358 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
这是不需要的,因为当主进程完成时,所有#0都关闭。
设置变量时,确保在=符号之前和/或之后有没有空格。我花了一个小时试图弄清楚这个问题,尝试各种解决方案!这是没有很酷。
=
更正:
WTFF=`echo "stuff"`echo "Example: $WTFF"
将失败错误:“东西:没有找到”或类似
WTFF= `echo "stuff"`echo "Example: $WTFF"
你也得用
$(command-here)
`command-here`
#!/bin/bash VAR1="$1"VAR2="$2" MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)" echo "$MOREF"
如果您尝试执行的命令失败,它会将输出写入错误流,然后打印到控制台。
要避免它,您必须重定向错误流:
result=$(ls -l something_that_does_not_exist 2>&1)
Mac/OSX现在带有旧的Bash版本,即GNU bash, version 3.2.57(1)-release (arm64-apple-darwin21)。在这种情况下,可以使用:
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin21)
new_variable="$(some_command)"
一个具体的例子:
newvar="$(echo $var | tr -d '123')"
注意(),而不是Bash 4中通常的{}。
()
{}