it-swarm.cn

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

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

#!/bin/bash

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

echo $MOREF

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

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

1317
John

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

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

MULTILINE=$(ls \
   -1)
echo "${MULTILINE}"

引用(")对保留多行值很重要。

1989
Andy Lester

正确的方法是

$(Sudo run command)

如果你要使用撇号,你需要`,而不是'。这个角色被称为“反叛”(或“严重重音”)。

像这样:

#!/bin/bash

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

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

echo "$MOREF"
240
Ilya Kogan

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

提议的替代方法$(command)也可以工作,并且它也更容易阅读,但请注意它只对Bash或KornShell(以及从那些派生的shell)有效,所以如果你的脚本必须在各种Unix系统上真正可移植,你应该更喜欢旧的反引号符号。

64
bitwelder

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

2nd编辑2018-02-12:添加不同的方式,在底部搜索长时间运行的任务

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

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

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

读取多个变量(使用bashisms

df -k /
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/dm-0         999320 529020    401488  57% /

如果我只想要Used值:

array=($(df -k /))

你可以看到array变量:

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只会skip标题行(变量$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 line不是必需的,只是为了便于阅读。

关于Sudo cmd | grep ... | cut ...

Shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $Shell
/bin/bash

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

Shell=$(grep $USER </etc/passwd | cut -d : -f 7)

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

所以使用sed作为样本,会将子进程限制为只有一个fork

Shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $Shell

并使用bashisms

但对于许多操作,主要是小文件, 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 ...

进一步关于变量分裂 ......

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

替代方案:使用后台长期运行任务减少forks

第二编辑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)

这工作正常,但是运行许多叉子很重且很慢。

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

看到:

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

date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288

所以我们可以使用long running后台进程来创建很多作业,而不必为每个请求启动一个新的fork

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

mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc

(当然,FD 56必须不使用!)......从那里,您可以通过以下方式使用此过程:

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

进入函数newConnector

您可以在 GitHub.Com我自己的网站 上找到我的newConnector函数(在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"

函数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在主要过程结束时关闭。

59
F. Hauri

我知道三种方法:

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

func (){
ls -l
}

通过说func来调用它

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

var="ls -l"
eval $var

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

var=$(ls -l)
OR
var=`ls -l`

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

echo "$var"

还有令人讨厌的方式:

echo $var
52
MLSC

只是为了与众不同:

MOREF=$(Sudo run command against $VAR1 | grep name | cut -c7-)
24
DigitalRoss

如果你想用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 [email protected]$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"
13
Jahid

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

正确: /

WTFF=`echo "stuff"`
echo "Example: $WTFF"

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

WTFF= `echo "stuff"`
echo "Example: $WTFF"
13
Emil

你需要使用其中之一

$(command-here)

要么

`command-here`

#!/bin/bash

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

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

echo "$MOREF"
8
Diego Velez

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

read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"
6
Aquarius Power

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

OUTPUT=$(x+2);
OUTPUT=`x+2`;

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

6
Pratik Patil

有些人可能觉得这很有用。变量替换中的整数值,其中诀窍是使用$(())双括号:

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
4
Gus

还有两种方法:

请记住,空间在 bash 中非常重要。因此,如果您希望命令运行,请按原样使用,而不再引入任何空格。

  1. 以下将harshil分配给L然后打印出来

    L=$"harshil"
    echo "$L"
    
  2. 下面将命令tr的输出分配给L2。 tr正在另一个变量L1上运行。

    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    
4
Harshil