如何在Bash中将变量设置为命令的输出?

Joh*_*ohn 1513 bash shell command-line

我有一个非常简单的脚本,如下所示:

#!/bin/bash

VAR1="$1"
MOREF='sudo run command against $VAR1 | grep name | cut -c7-'

echo $MOREF
Run Code Online (Sandbox Code Playgroud)

当我从命令行运行此脚本并将参数传递给它时,我没有得到任何输出.但是,当我运行$MOREF变量中包含的命令时,我能够获得输出.

我想知道如何获取需要在脚本中运行的命令的结果,将其保存到变量,然后在屏幕上输出该变量?

And*_*ter 2211

除了反引号(`command`),你可以使用$(command),我觉得更容易阅读,并允许嵌套.

OUTPUT="$(ls -1)"
echo "${OUTPUT}"

MULTILINE=$(ls \
   -1)
echo "${MULTILINE}"
Run Code Online (Sandbox Code Playgroud)

Quoting("$(command)")对保留多行值很重要.

  • 仅供参考这称为"命令替换":http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution (71认同)
  • 我们能为多线输出提供一些分离器吗? (55认同)
  • 白色空间(或缺少空白)很重要 (18认同)
  • 当变量后面跟着更多可以解释为变量名称一部分的字符时,可以使用大括号.*例如*`$ {OUTPUT} foo`.在对变量执行内联字符串操作时也需要它们,例如`$ {OUTPUT/foo/bar}` (12认同)
  • @ timhc22,花括号无关紧要; 它只是重要的引号:在传递给`echo`命令之前,扩展结果是否是字符串拆分和全局扩展. (8认同)
  • 所以 `echo "${OUTPUT}"` 会保留换行符,而 `echo $OUTPUT` 不会? (2认同)
  • 啊,谢谢!那么花括号有什么好处吗? (2认同)
  • 另外:OUTPUT,=和引号之间不允许有空格.这让我失望了. (2认同)

Ily*_*gan 265

更新(2018年):正确的方法是

$(sudo run command)
Run Code Online (Sandbox Code Playgroud)

你使用的是错误的撇号.你需要`,没有'.这个角色被称为"反叛"(或"重音").

像这样:

#!/bin/bash

VAR1="$1"
VAR2="$2"

MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`

echo "$MOREF"
Run Code Online (Sandbox Code Playgroud)

  • 反引号语法是过时的,你真的需要在`echo`中的变量插值周围加上双引号. (28认同)
  • 我想补充一点,你必须小心上面赋值中'='周围的空格.你*在那里没有任何空格*,否则你将得到一个错误的任务 (9认同)
  • tripleeee的评论是正确的.在cygwin(2016年5月)中,``不起作用而`$()`有效.在看到此页面之前无法修复. (4认同)
  • 关于**更新(2018)**的例子的详细说明将不胜感激. (2认同)

小智 72

正如他们已经向你指出的那样,你应该使用'反引号'.

提出的替代方案$(command)也有效,并且它也更容易阅读,但请注意它仅对bash或korn shell(以及从这些shell派生的shell)有效,因此如果您的脚本必须在各种Unix系统上真正可移植,那么您应该更喜欢旧的反引号符号.

  • 他们非常谨慎.很久以前POSIX已经弃用了反引号; 在这个千年的大多数炮弹中应该有更现代的语法.(仍然存在传统环境*咳嗽*HP-UX*咳嗽*,它们在九十年代初期坚定不移.) (23认同)
  • 不正确.`$()`与二十年前标准化的POSIX sh完全兼容. (22认同)
  • @JonathanLeffler 实际上,在 Solaris 11 中,`/bin/sh` 是 `ksh93` 的情况已经不同了。 (3认同)
  • 请注意,Solaris 10上的`/ bin/sh`仍然无法识别`$(...)` - 以及在Solaris 11上也是如此的AFAIK. (2认同)
  • @tripleee - 响应晚了三年 :-) 但我在过去 10 多年里一直在 HP-UX 的 POSIX shell 中使用 `$()`。 (2认同)
  • 只是想添加 `$(\`command\`)` 会将命令输出解释为命令 (2认同)

F. *_*uri 66

我用来从命令设置变量的一些技巧

第二次编辑2018-02-12:添加一种不同的方式,在此底部搜索长时间运行的任务!

2018-01-25编辑:添加示例函数(用于填充有关磁盘使用情况的变量)

第一个简单的旧和兼容的方式

myPi=`echo '4*a(1)' | bc -l`
echo $myPi 
3.14159265358979323844
Run Code Online (Sandbox Code Playgroud)

大多数兼容,第二种方式

由于嵌套可能变得很重,因此为此实现了括号

myPi=$(bc -l <<<'4*a(1)')
Run Code Online (Sandbox Code Playgroud)

嵌套样本:

SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted 
1480656334
Run Code Online (Sandbox Code Playgroud)

读取多个变量(用bashisms)

df -k /
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/dm-0         999320 529020    401488  57% /
Run Code Online (Sandbox Code Playgroud)

如果我只想要使用值:

array=($(df -k /))
Run Code Online (Sandbox Code Playgroud)

你可以看到数组变量:

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]="/")'
Run Code Online (Sandbox Code Playgroud)

然后:

echo ${array[9]}
529020
Run Code Online (Sandbox Code Playgroud)

但我更喜欢这个:

{ read foo ; Read filesystem size using avail prct mountpoint ; } < <(df -k /)
echo $used
529020
Run Code Online (Sandbox Code Playgroud)

1st 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
Run Code Online (Sandbox Code Playgroud)

Nota:declare行不是必需的,只是为了便于阅读.

关于 sudo cmd | grep ... | cut ...

shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash
Run Code Online (Sandbox Code Playgroud)

(请避免无用cat!所以这只是1叉少:

shell=$(grep $USER </etc/passwd | cut -d : -f 7)
Run Code Online (Sandbox Code Playgroud)

所有管道(|)都表示叉子.必须运行另一个进程,访问磁盘,库调用等.

所以使用sedfor sample,会将subprocess限制为只有一个fork:

shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
Run Code Online (Sandbox Code Playgroud)

bashisms:

但对于许多操作,主要是小文件,可以自己完成这项工作:

while IFS=: read -a line ; do
    [ "$line" = "$USER" ] && shell=${line[6]}
  done </etc/passwd
echo $shell
/bin/bash
Run Code Online (Sandbox Code Playgroud)

要么

while IFS=: read loginname encpass uid gid fullname home shell;do
    [ "$loginname" = "$USER" ] && break
  done </etc/passwd
echo $shell $loginname ...
Run Code Online (Sandbox Code Playgroud)

进一步讨论变量分裂 ......

看看我如何在Bash中的分隔符上拆分字符串的答案

替代方案:使用后台长时间运行的任务来减少货叉

第2编辑2018-02-12: 为了防止像多个叉子一样

myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")
Run Code Online (Sandbox Code Playgroud)

要么

myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)
Run Code Online (Sandbox Code Playgroud)

这工作正常,但运行许多叉子是沉重和缓慢.

和命令,date并且bc可以一行一行地进行许多操作!

看到:

bc -l <<<$'3*4\n5*6'
12
30

date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Run Code Online (Sandbox Code Playgroud)

因此,我们可以使用长时间运行的后台进程来创建许多作业,而无需为每个请求启动新的fork.

我们只需要一些文件描述符fifos来正确执行此操作:

mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc
Run Code Online (Sandbox Code Playgroud)

(当然,FD 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
Run Code Online (Sandbox Code Playgroud)

成功能 newConnector

您可以newConnectorGitHub.Com我自己的网站上找到我的功能(在github上的Nota ,我的网站上有两个文件,功能和演示被捆绑到一个文件中,可以使用或只运行演示)

样品:

. 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"
Run Code Online (Sandbox Code Playgroud)

该函数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
Run Code Online (Sandbox Code Playgroud)

从那里,如果你想结束一个后台进程,你只需要关闭他的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
Run Code Online (Sandbox Code Playgroud)

这是不需要的,因为当主要过程结束时所有fd都关闭.

  • 最好不要使用所有“编辑”注释和删除线(这就是修订历史记录的用途),就好像这个答案是今天写的一样。如果需要吸取一些教训,可以将其记录在一个部分中,例如*“不要做的事情”*。 (3认同)
  • @ capricorn1这是[echo的无用用法](http://www.iki.fi/era/unix/award.html#echo); 您只需要`grep“ $ CONTAINER_NAME”` (2认同)
  • @Cadoiz是的,有一些拼写错误...`read _`到*drop*不仅*skip*...所以...答案已编辑,添加了CSV解析器示例的链接。谢谢! (2认同)

MLS*_*LSC 53

我知道三种方法:

1)功能适用于此类任务:

func (){
    ls -l
}
Run Code Online (Sandbox Code Playgroud)

通过说出来调用它 func

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

var="ls -l"
eval $var
Run Code Online (Sandbox Code Playgroud)

3)第三个是直接使用变量:

var=$(ls -l)

    OR

var=`ls -l`
Run Code Online (Sandbox Code Playgroud)

你可以很好地得到第三种解决方案的输出:

echo "$var"
Run Code Online (Sandbox Code Playgroud)

还有令人讨厌的方式:

echo $var
Run Code Online (Sandbox Code Playgroud)

  • 作为 bash 的新手,为什么 `"$var"` 很好,而 `$var` 很糟糕? (3认同)

Dig*_*oss 27

只是为了与众不同:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
Run Code Online (Sandbox Code Playgroud)


Emi*_*mil 18

设置变量时,请确保在=符号之前和/或之后没有空格.花了一个小时试图弄清楚这一点,尝试各种解决方案!这不酷.

正确:

WTFF=`echo "stuff"`
echo "Example: $WTFF"
Run Code Online (Sandbox Code Playgroud)

将失败并出现错误:(东西:未找到或类似)

WTFF= `echo "stuff"`
echo "Example: $WTFF"
Run Code Online (Sandbox Code Playgroud)

  • 带有空格的版本*意味着不同*:`var=value somecommand` 在其环境中运行`somecommand`,`var` 的值为`value`。因此,`var= somecommand` 在 `somecommand` 的环境中以空(零字节)值导出 `var`。 (3认同)

Jah*_*hid 14

如果你想用multiline/multiple命令执行它,那么你可以这样做:

output=$( bash <<EOF
# Multiline/multiple command/s
EOF
)
Run Code Online (Sandbox Code Playgroud)

要么:

output=$(
# Multiline/multiple command/s
)
Run Code Online (Sandbox Code Playgroud)

例:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"
Run Code Online (Sandbox Code Playgroud)

输出:

first
second
third
Run Code Online (Sandbox Code Playgroud)

使用heredoc,您可以通过将长单行代码分解为多行代码来轻松简化操作.另一个例子:

output="$( ssh -p $port $user@$domain <<EOF
# Breakdown your long ssh command into multiline here.
EOF
)"
Run Code Online (Sandbox Code Playgroud)

  • 命令替换中的第二个"bash"是什么?您已经通过命令替换本身创建子shell.如果要放置多个命令,只需用换行符或分号分隔即可.`output = $(echo first; echo second; ...)` (2认同)
  • 当我使用sred的heredoc时,我精确地命令运行`ssh -p $ port $ user @ $ domain/bin/bash << EOF`以防止`因为stdin不是终端,所以不会分配伪终端警告 (2认同)

Die*_*lez 9

你需要使用其中之一

$(command-here)

要么

`command-here`
Run Code Online (Sandbox Code Playgroud)

#!/bin/bash

VAR1="$1"
VAR2="$2"

MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"

echo "$MOREF"
Run Code Online (Sandbox Code Playgroud)

  • $()比反引号要好得多。请参阅:[在shell脚本中使用$()代替反引号有什么好处?](/sf/ask/661484491/) (2认同)

zab*_*bop 7

如今,Mac/OSX 带有旧的 Bash 版本,即GNU bash, version 3.2.57(1)-release (arm64-apple-darwin21). 在这种情况下,可以使用:

new_variable="$(some_command)"
Run Code Online (Sandbox Code Playgroud)

一个具体的例子:

newvar="$(echo $var | tr -d '123')"
Run Code Online (Sandbox Code Playgroud)

请注意(), ,而不是{}Bash 4 中常见的符号。


Aqu*_*wer 6

这是另一种方式,适用于某些文本编辑器,这些编辑器无法正确突出显示您创建的每个复杂代码.

read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"
Run Code Online (Sandbox Code Playgroud)


Pra*_*til 6

你可以使用反蜱(也称为重点坟墓)或$().像 - 作为 -

OUTPUT=$(x+2);
OUTPUT=`x+2`;
Run Code Online (Sandbox Code Playgroud)

两者都有相同的效果.但OUTPUT = $(x + 2)更易读,也是最新的.

  • 实现括号以允许嵌套. (2认同)
  • 在大多数地方,“x+2”不是有效的命令。在某种程度上,这不会误导初学者认为这就是算术的方式,这重复了现有的答案。 (2认同)

caf*_*991 6

如果您尝试执行的命令失败,它会将输出写入错误流,然后打印到控制台。

为了避免它,您必须重定向错误流:

result=$(ls -l something_that_does_not_exist 2>&1)
Run Code Online (Sandbox Code Playgroud)


小智 5

这里还有两种方法:

请记住,空间在 Bash 中非常重要。因此,如果您希望命令运行,请按原样使用,而无需引入更多空格。

  1. 下面赋值harshilL然后打印出来

    L=$"harshil"
    echo "$L"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 下面将命令的输出分配tr给 L2。tr正在对另一个变量 L1 进行操作。

    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    
    Run Code Online (Sandbox Code Playgroud)

  • 1. [`$"..."` 可能不像你认为的那样](https://www.gnu.org/software/bash/manual/bashref.html#Locale-Translation)。2. 这已经在安迪莱斯特的回答中给出了。 (4认同)

归档时间:

查看次数:

1703001 次

最近记录:

5 年,9 月 前