如何在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:03:48  来源:igfitidea点击:

How do I set a variable to the output of a command in Bash?

bashshellcommand-line

提问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 $MOREFvariable, 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 foowill 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: declareline 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 sedfor sample, will limit subprocess to only one fork:

因此,使用sedfor 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 dateand bccould make many operations, line by line!!

datebc这样的命令可以逐行进行许多操作!!

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 5and 6have to be unused!)... From there, you could use this process by:

(当然,FD56必须是未使用的!)......从那里,你可以通过使用此过程:

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 newConnectorfunction 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.)

你可以newConnectorGitHub.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 myBclets 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:

我知道三种方法:

  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`
    
  1. 函数适用于此类任务:**

    func (){
        ls -l
    }
    

    通过说来调用它func

  2. 另一个合适的解决方案可能是 eval:

    var="ls -l"
    eval $var
    
  3. 第三种是直接使用变量:

    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) 更具可读性并且是最新的。