首页 文章

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

提问于
浏览
1200

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

#!/bin/bash

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

echo $MOREF

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

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

13 回答

  • 12

    你需要使用其中之一

    $(command-here)

    要么

    `command-here`
    

    #!/bin/bash
    
    VAR1="$1"
    VAR2="$2"
    
    MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"
    
    echo "$MOREF"
    
  • 0

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

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

    两者都有相同的效果 . 但OUTPUT = $(x 2)更具可读性和最新可读性 .

  • 48

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

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

  • 22

    我知道三种方法:

    1) Functions are suitable for such tasks:

    func (){
    ls -l
    }
    

    通过说 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`
    

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

    echo "$var"
    

    还有令人讨厌的方式:

    echo $var
    
  • 4

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

    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
    
  • 43

    Update (2018):正确的方法是

    $(sudo run command)
    

    你使用的是错误的撇号 . 你需要 ``` ,而不是 ' . 此字符称为"backticks"(或"grave accent") .

    像这样:

    #!/bin/bash
    
    VAR1="$1"
    VAR2="$2"
    
    MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`
    
    echo "$MOREF"
    
  • 3

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

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

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

  • 5

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

    2nd Edit 2018-02-12: Adding a special way, see at very bottom of this!

    2018-01-25 Edit: add sample function (用于填充有关磁盘使用情况的变量)

    第一个简单的旧兼容方式

    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% /
    

    如果我只想要使用值:

    array=($(df -k /))
    

    你可以看到数组变量:

    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 将跳过 Headers 行(变量 $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 行不是必需的,只是为了便于阅读 .

    关于sudo cmd | grep ... |切...

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

    关于变量分裂的进一步说明......

    看看我对How do I split a string on a delimiter in Bash?的回答

    备选:使用后台长时间运行的任务减少分叉

    2nd Edit 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
    

    我们可以使用长时间运行的后台进程重复创建作业,而不必为每个请求启动新的fork:

    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.Commy own site上找到我的 newConnector 函数(在github上有Nota,在我的网站上有两个文件,函数和演示被捆绑到1个文件中,可以使用或仅运行演示)

    样品:

    . 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

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

    正确:

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

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

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

    只是为了与众不同:

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

    还有两种方法:

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

    • 以下将harshil分配给L然后打印出来
    L=$"harshil"
    echo "$L"
    
    • 以下将命令 tr 的输出分配给L2 . tr 正在另一个变量L1上运行 .
    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    
  • 1819

    如果你想用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 $user@$domain <<EOF 
    #breakdown your long ssh command into multiline here.
    EOF
    )"
    
  • 6

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

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

相关问题