代码之家  ›  专栏  ›  技术社区  ›  ldog

从管道将值读入shell变量

  •  169
  • ldog  · 技术社区  · 16 年前

    echo "hello world" | test=($(< /dev/stdin)); echo test=$test
    test=
    
    echo "hello world" | read test; echo test=$test
    test=
    
    echo "hello world" | test=`cat`; echo test=$test
    test=
    

    test=hello world "$test" 那也不行。

    14 回复  |  直到 8 年前
        1
  •  178
  •   fedorqui    12 年前

    使用

    IFS= read var << EOF
    $(foo)
    EOF
    

    你呢 戏法 read

    echo "hello world" | { read test; echo test=$test; }
    

    或者写一个这样的函数:

    read_from_pipe() { read "$@" <&0; }
    

    阅读 不用管输入-它是未定义的。

    仅供参考, http://www.etalabs.net/sh_tricks.html 是一个漂亮的收集必需的原油,以打击古怪和不相容的伯恩贝壳,上海。

        2
  •  116
  •   Nick    16 年前

    如果您想读入大量数据并分别处理每一行,可以使用以下方法:

    cat myFile | while read x ; do echo $x ; done
    

    cat myFile | while read x y ; do echo $y $x ; done
    

    while read x y ; do echo $y $x ; done < myFile
    

    但是,一旦你开始想用这类东西做任何真正聪明的事情,你最好使用一些脚本语言,比如perl,你可以尝试这样的方法:

    perl -ane 'print "$F[0]\n"' < myFile
    

        3
  •  53
  •   Zombo tliff    11 年前

    这是另一种选择

    $ read test < <(echo hello world)
    
    $ echo $test
    hello world
    
        4
  •  45
  •   Dennis Williamson    7 年前

    read 不会从管道中读取(或者可能由于管道创建子shell而导致结果丢失)。但是,可以在Bash中使用here字符串:

    $ read a b c <<< $(echo 1 2 3)
    $ echo $a $b $c
    1 2 3
    

    但是,请参阅@chepner的答案,以获取有关 lastpipe .

        5
  •  41
  •   djanowski    13 年前

    我不是Bash方面的专家,但我想知道为什么没有人提出这一点:

    stdin=$(cat)
    
    echo "$stdin"
    

    $ fortune | eval 'stdin=$(cat); echo "$stdin"'
    
        6
  •  27
  •   chepner    12 年前

    bash lastpipe 选项,通过在当前shell的管道中执行最后一个命令,而不是在子shell中执行最后一个命令,您的代码可以按编写的方式工作。

    shopt -s lastpipe
    echo "hello world" | read test; echo test=$test
    
        7
  •  15
  •   mob    16 年前

    从shell命令到bash变量的隐式管道的语法如下

    var=$(command)
    

    或

    var=`command`
    

        8
  •  15
  •   Seff    5 年前

    可以从管道参数和命令行参数读取数据的智能脚本:

    #!/bin/bash
    if [[ -p /dev/stdin ]]
        then
        PIPE=$(cat -)
        echo "PIPE=$PIPE"
    fi
    echo "ARGS=$@"
    

    输出:

    $ bash test arg1 arg2
    ARGS=arg1 arg2
    
    $ echo pipe_data1 | bash test arg1 arg2
    PIPE=pipe_data1
    ARGS=arg1 arg2
    

    当脚本通过管道接收任何数据时,/dev/stdin(或/proc/self/fd/0)将是管道的符号链接。

    /proc/self/fd/0 -> pipe:[155938]
    

    如果不是,它将指向当前终端:

    /proc/self/fd/0 -> /dev/pts/5
    

    狂欢节 [[ -p 选项可以检查它是否是管道。

    cat - 从中读取 stdin

    如果我们使用 猫- 当没有 ,它将永远等待,这就是为什么我们把它放在 if 条件。

        9
  •  10
  •   jbuhacoff    13 年前

    第一次尝试非常接近。这种变化应该起作用:

    echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };
    

    输出为:

    test=你好世界

    管道后面需要大括号来括起要测试的赋值和回显。

        10
  •  9
  •   Jaleks    10 年前

    在我看来,在bash中读取stdin的最佳方法是以下方法,它还允许您在输入结束之前处理行:

    while read LINE; do
        echo $LINE
    done < /dev/stdin
    
        11
  •  7
  •   hd-quadrat    9 年前

    因为我上当了,我想写张纸条。 我找到了这个线索,因为我要重写一个旧的sh脚本 与POSIX兼容。 这基本上意味着通过重写如下代码来绕过POSIX引入的管道/子shell问题:

    some_command | read a b c
    

    分为:

    read a b c << EOF
    $(some_command)
    EOF
    

    代码如下:

    some_command |
    while read a b c; do
        # something
    done
    

    分为:

    while read a b c; do
        # something
    done << EOF
    $(some_command)
    EOF
    

    使用旧的表示法,while循环不是在空输入上输入的, 但在POSIX符号中它是! 这是不可能的。 POSIX代码的行为更像旧的表示法 看起来像这样:

    while read a b c; do
        case $a in ("") break; esac
        # something
    done << EOF
    $(some_command)
    EOF
    

    在大多数情况下,这应该足够好了。 如果某个命令打印空行。 在旧的表示法中,while主体被执行 在POSIX符号中,我们在身体前面断裂。

    解决此问题的方法可能如下所示:

    while read a b c; do
        case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
        # something
    done << EOF
    $(some_command)
    echo "something_guaranteed_not_to_be_printed_by_some_command"
    EOF
    
        12
  •  3
  •   bta    16 年前

    将某个内容管道化到包含赋值的表达式中并不是这样。

    相反,请尝试:

    test=$(echo "hello world"); echo test=$test
    
        13
  •  3
  •   Community Mohan Dere    9 年前

    echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )
    

    也会工作,但它会打开另一个新的子壳后,管道,在哪里

    echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }
    

    不会。


    我不得不禁用作业控制来利用 chepnars '方法(我从终端运行此命令):

    set +m;shopt -s lastpipe
    echo "hello world" | read test; echo test=$test
    echo "hello world" | test="$(</dev/stdin)"; echo test=$test
    

    Bash Manual says :

    如果设置,以及 作业控制未激活 ,shell运行最后一个命令 当前shell中未在后台执行的管道

    注意:在非交互式shell中,默认情况下,作业控制处于关闭状态,因此您不需要 set +m 在剧本里。

        14
  •  1
  •   Mathieu J.    14 年前

    我想你想写一个shell脚本,可以从stdin获取输入。 但是,当您尝试内联执行时,您在尝试创建test=变量时迷失了方向。 我认为内联执行没有多大意义,这就是为什么它没有按您期望的方式工作。

    我想减少

    $( ... | head -n $X | tail -n 1 )
    

    从各种输入中获取特定行。 所以我可以打字。。。

    cat program_file.c | line 34
    

    22:14 ~ $ cat ~/bin/line 
    #!/bin/sh
    
    if [ $# -ne 1 ]; then echo enter a line number to display; exit; fi
    cat | head -n $1 | tail -n 1
    22:16 ~ $ 
    

    给你。

        15
  •  0
  •   Jimmix    5 年前

    我想出了一个如下的解决方案 #!/bin/sh 作为 #!/bin/bash )

    #!/bin/sh
    
    set -eu
    
    my_func() {
        local content=""
        
        # if the first param is an empty string or is not set
        if [ -z ${1+x} ]; then
     
            # read content from a pipe if passed or from a user input if not passed
            while read line; do content="${content}$line"; done < /dev/stdin
    
        # first param was set (it may be an empty string)
        else
            content="$1"
        fi
    
        echo "Content: '$content'"; 
    }
    
    
    printf "0. $(my_func "")\n"
    printf "1. $(my_func "one")\n"
    printf "2. $(echo "two" | my_func)\n"
    printf "3. $(my_func)\n"
    printf "End\n"
    

    输出:

    0. Content: ''
    1. Content: 'one'
    2. Content: 'two'
    typed text
    3. Content: 'typed text'
    End
    

    对于最后一种情况(3),您需要键入,按enter键并按CTRL+D结束输入。

        16
  •  -1
  •   bingles    11 年前

    echo "hello world" | echo test=$(cat)