如何在 Bash 中为命令的输出设置变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4651437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I set a variable to the output of a command in Bash?
提问by John
I have a pretty simple script that is something like the following:
我有一个非常简单的脚本,类似于以下内容:
#!/bin/bash
VAR1=""
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.
当我从命令行运行这个脚本并将参数传递给它时,我没有得到任何输出。但是,当我运行$MOREF
变量中包含的命令时,我能够获得输出。
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?
如何获取需要在脚本中运行的命令的结果,将其保存到变量中,然后在屏幕上输出该变量?
回答by Andy Lester
In addition to backticks `command`
, command substitutioncan be done with $(command)
or "$(command)"
, which I find easier to read, and allows for nesting.
除了反引号`command`
,命令替换还可以使用$(command)
or完成"$(command)"
,我觉得这更容易阅读,并且允许嵌套。
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.
引用 ( "
) 对保留多行变量值很重要;它在作业的右侧是可选的,因为不执行分词,因此OUTPUT=$(ls -1)
可以正常工作。
回答by Ilya Kogan
The right way is
正确的方法是
$(sudo run command)
If you're going to use an apostrophe, you need `
, not '
. This character is called "backticks" (or "grave accent").
如果要使用撇号,则需要`
,而不是'
。这个字符被称为“反引号”(或“重音符”)。
Like this:
像这样:
#!/bin/bash
VAR1=""
VAR2=""
MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`
echo "$MOREF"
回答by F. Hauri
Some Bashtricks I use to set variables from commands
我用来从命令设置变量的一些Bash技巧
2nd Edit 2018-02-12: Added a different way, search at the bottom of this for long-running tasks!
第二次编辑 2018-02-12:添加了不同的方式,在此底部搜索长期运行的任务!
2018-01-25 Edit: added a sample function(for populating variables about disk usage)
2018-01-25 编辑:添加了一个示例函数(用于填充有关磁盘使用情况的变量)
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)
读取多个变量(使用Bashisms)
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
If I just want a usedvalue:
如果我只想要一个使用过的值:
array=($(df -k /))
you could see an arrayvariable:
你可以看到一个数组变量:
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 using avail prct mountpoint ; } < <(df -k /)
echo $using
529020
The first read foo
will just skipheader line, but in only onecommand, you will populate 7 differentvariables:
第一个read foo
将跳过标题行,但仅在一个命令中,您将填充 7 个不同的变量:
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 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")
... will work with associative arraystoo: read foo disk[total] disk[used] ...
...也适用于关联数组:read foo disk[total] disk[used] ...
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
echo $total $used $free
Nota: declare
line is not required, just for readability.
注意:declare
行不是必需的,只是为了可读性。
About sudo cmd | grep ... | cut ...
关于 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:
(请避免无用cat
!所以这只是少了一个叉子:
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:
因此,使用sed
for sample 会将子进程限制为只有一个fork:
shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
And with Bashisms:
和Bashisms:
But for many actions, mostly on small files, Bash could do the job itself:
但是对于许多操作,主要是在小文件上,Bash 可以自己完成这项工作:
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?
看看我对如何在 Bash 中的分隔符上拆分字符串的回答?
Alternative: reducing forksby using backgrounded long-running tasks
替代方案:通过使用后台长时间运行的任务来减少分叉
2nd Edit 2018-02-12:
第二次编辑 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)
This work fine, but running many forks is heavy and slow.
这工作得很好,但运行许多叉子很重而且很慢。
And commands like date
and bc
could make many operations, line by line!!
像date
和bc
这样的命令可以逐行进行许多操作!!
See:
看:
bc -l <<<$'3*4\n5*6'
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
So we could use a long runningbackground process to make many jobs, without having to initiate a new forkfor each request.
所以我们可以使用一个长时间运行的后台进程来做很多工作,而不必为每个请求启动一个新的分支。
We just need some file descriptorsand fifosfor doing this properly:
我们只需要一些文件描述符和fifos来正确执行此操作:
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:
(当然,FD5
和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
Into a function newConnector
成函数 newConnector
You may found my newConnector
function on GitHub.Comor on my own site(Note on GitHub: there are two files on my site. Function and demo are bundled into one file which could be sourced for use or just run for demo.)
你可以newConnector
在GitHub.Com或我自己的网站上找到我的函数(GitHub 上的注释:我的网站上有两个文件。函数和演示捆绑在一个文件中,可以提供使用或运行演示。)
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
lets you use the background task with simple syntax, and for date:
该函数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
From there, if you want to end one of background processes, you just have to close its fd:
从那里,如果你想结束后台进程之一,你只需要关闭它的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.
这是不需要的,因为当主进程完成时所有 fd 都会关闭。
回答by bitwelder
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.
建议的替代方案$(command)
也有效,而且更易于阅读,但请注意,它仅对 Bash 或 KornShell(以及从它们派生的 shell)有效,因此如果您的脚本必须在各种 Unix 系统上真正可移植,您应该更喜欢旧的反引号符号。
回答by MLSC
I know three ways to do it:
我知道三种方法:
Functions are suitable for such tasks:**
func (){ ls -l }
Invoke it by saying
func
.Also another suitable solution could be eval:
var="ls -l" eval $var
The third one is using variables directly:
var=$(ls -l) OR var=`ls -l`
函数适用于此类任务:**
func (){ ls -l }
通过说来调用它
func
。另一个合适的解决方案可能是 eval:
var="ls -l" eval $var
第三种是直接使用变量:
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
回答by DigitalRoss
Just to be different:
只是为了不同:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
回答by Emil
When setting a variable make sure you have NO Spacesbefore and/or after the =sign. 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 Failwith error "stuff: not found" or similar
将因错误“stuff: not found”或类似错误而失败
WTFF= `echo "stuff"`
echo "Example: $WTFF"
回答by Jahid
If you want to do it with multiline/multiple command/s then you can do this:
如果你想用 multiline/multiple command/s 来做,那么你可以这样做:
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:
使用heredoc,您可以通过将长单行代码分解为多行代码来轻松简化事情。另一个例子:
output="$( ssh -p $port $user@$domain <<EOF
# Breakdown your long ssh command into multiline here.
EOF
)"
回答by Diego Velez
You need to use either
你需要使用
$(command-here)
$(command-here)
or
或者
`command-here`
Example
例子
#!/bin/bash
VAR1=""
VAR2=""
MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"
echo "$MOREF"
回答by Pratik Patil
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.
两者具有相同的效果。但是 OUTPUT=$(x+2) 更具可读性并且是最新的。