从管道中将值读入shell变量

ldo*_*dog 193 linux bash pipe

我试图让bash处理来自stdin的数据,但是没有运气.我的意思是以下工作:

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

我想要输出的地方test=hello world.我试过把""引号括起来"$test"也不起作用.

yar*_*ena 159

使用

IFS= read var << EOF
$(foo)
EOF
Run Code Online (Sandbox Code Playgroud)

可以欺骗read从这样的管道接受:

echo "hello world" | { read test; echo test=$test; }
Run Code Online (Sandbox Code Playgroud)

甚至写一个这样的函数:

read_from_pipe() { read "$@" <&0; }
Run Code Online (Sandbox Code Playgroud)

但没有意义 - 你的变量任务可能不会持久!管道可以生成子shell,其中环境由值继承,而不是通过引用继承.这就是为什么read不打扰来自管道的输入 - 它是未定义的.

仅供参考,http://www.etalabs.net/sh_tricks.html是为了对抗伯恩贝壳的奇怪和不相容性所必需的琐事.

  • 诀窍不在于从管道中获取"读取"输入,而是在执行"读取"的同一个shell中使用变量. (3认同)
  • 让我们再试一次(显然在这个标记中逃避反引号很有趣):`test = \`echo"hello world"| {阅读测试; echo $ test; } \`` (2认同)

Nic*_*ick 105

如果你想要读取大量数据并分别在每一行上工作,你可以使用这样的东西:

cat myFile | while read x ; do echo $x ; done
Run Code Online (Sandbox Code Playgroud)

如果你想将这些行分成多个单词,你可以使用多个变量代替x,如下所示:

cat myFile | while read x y ; do echo $y $x ; done
Run Code Online (Sandbox Code Playgroud)

或者:

while read x y ; do echo $y $x ; done < myFile
Run Code Online (Sandbox Code Playgroud)

但是一旦你开始想要做任何非常聪明的事情,你最好选择像perl这样的脚本语言,你可以尝试这样的事情:

perl -ane 'print "$F[0]\n"' < myFile
Run Code Online (Sandbox Code Playgroud)

perl(或者我猜这些语言中的任何一种语言)有一个相当陡峭的学习曲线但是如果你想做除了最简单的脚本之外的任何事情,从长远来看你会发现它更容易.我推荐Perl Cookbook,当然还有Larry Wall等人的Perl编程语言.

  • "替代"是正确的方法.没有UUoC也没有子shell.参见[BashFAQ/024](http://mywiki.wooledge.org/BashFAQ/024). (8认同)

Pau*_*ce. 40

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

$ read a b c <<< $(echo 1 2 3)
$ echo $a $b $c
1 2 3
Run Code Online (Sandbox Code Playgroud)

  • 漂亮又简单的一句台词,很容易理解。这个答案需要更多的赞成票。 (3认同)
  • `&lt;&lt;&lt;` 添加可能不需要的换行符 (2认同)

小智 39

这是另一种选择

$ read test < <(echo hello world)

$ echo $test
hello world
Run Code Online (Sandbox Code Playgroud)

  • `((..)`超过`$(..)`的显着优点是`<(..)`一旦它执行的命令使其可用,就会将每一行返回给调用者.但是,`$(..)`等待命令完成并生成所有输出,然后才能向调用者提供任何输出. (21认同)

dja*_*ski 30

我不是Bash的专家,但我想知道为什么没有提出这个:

stdin=$(cat)

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

单线证明它对我有用:

$ fortune | eval 'stdin=$(cat); echo "$stdin"'
Run Code Online (Sandbox Code Playgroud)

  • 有时简单明了特朗普效率:) (8认同)
  • 这可能是因为"read"是一个bash命令,而cat是一个单独的二进制文件,将在子进程中启动,因此效率较低. (4认同)
  • 绝对是最直接的答案 (4认同)
  • 在寻找已接受答案的替代方案时,我决定为我的用例采用与此答案类似的内容:)``${@:-$(cat)}``` (2认同)

che*_*ner 25

bash4.2引入了一个lastpipe选项,它允许您的代码按照写入的方式工作,方法是在当前shell的管道中执行最后一个命令,而不是子shell.

shopt -s lastpipe
echo "hello world" | read test; echo test=$test
Run Code Online (Sandbox Code Playgroud)

  • 啊!太好了,这个。如果在交互式 shell 中进行测试,还可以:“set +m”(在 .sh 脚本中不需要) (2认同)

Sef*_*eff 15

一个可以从 PIPE 和命令行参数读取数据的智能脚本:

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

输出:

$ bash test arg1 arg2
ARGS=arg1 arg2

$ echo pipe_data1 | bash test arg1 arg2
PIPE=pipe_data1
ARGS=arg1 arg2
Run Code Online (Sandbox Code Playgroud)

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

/proc/self/fd/0 -> pipe:[155938]
Run Code Online (Sandbox Code Playgroud)

如果没有,它将指向当前终端:

/proc/self/fd/0 -> /dev/pts/5
Run Code Online (Sandbox Code Playgroud)

bash[[ -p选项可以检查它是否是管道。

cat -读取自stdin.

如果我们cat -在没有时使用stdin,它将永远等待,这就是我们将它放在if条件中的原因。

  • 您还可以使用“/dev/stdin”,它是“/proc/self/fd/0”的链接 (2认同)

mob*_*mob 13

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

var=$(command)
Run Code Online (Sandbox Code Playgroud)

要么

var=`command`
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您将数据传递给赋值语句,该语句不期望任何输入.

  • 第一种形式var = $(command)更可取. (6认同)
  • 因为$()可以很容易地嵌套.想想JAVA_DIR = $(dirname $(readlink -f $(java))),并用`来试试.你需要逃脱三次! (4认同)

jbu*_*off 6

第一次尝试非常接近.这种变化应该有效:

echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };
Run Code Online (Sandbox Code Playgroud)

输出是:

test =你好世界

在管道之后需要括号来包含要测试的分配和回声.

没有大括号,测试(在管道之后)的分配在一个shell中,而echo"test = $ test"在一个单独的shell中,它不知道该分配.这就是为什么你在输出中得到"test ="而不是"test = hello world".


Jal*_*eks 6

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

while read LINE; do
    echo $LINE
done < /dev/stdin
Run Code Online (Sandbox Code Playgroud)


hd-*_*rat 6

因为我爱上了它,所以我想写个便条。我找到了这个线程,因为我必须重写一个旧的sh脚本才能与POSIX兼容。这基本上意味着通过重写如下代码来避免POSIX引入的管道/子外壳问题:

some_command | read a b c
Run Code Online (Sandbox Code Playgroud)

变成:

read a b c << EOF
$(some_command)
EOF
Run Code Online (Sandbox Code Playgroud)

像这样的代码:

some_command |
while read a b c; do
    # something
done
Run Code Online (Sandbox Code Playgroud)

变成:

while read a b c; do
    # something
done << EOF
$(some_command)
EOF
Run Code Online (Sandbox Code Playgroud)

但是后者在空输入时的行为并不相同。使用旧的表示法时,while循环不会在空输入中输入,但是在POSIX表示法中是!我认为这是由于EOF之前的换行符所致,因此不能忽略。行为更像旧符号的POSIX代码如下所示:

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

在大多数情况下,这应该足够好。但是不幸的是,如果some_command打印一个空行,它的行为仍然与旧记法并不完全一样。在旧的表示法中,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
Run Code Online (Sandbox Code Playgroud)


bta*_*bta 5

将某些内容通过管道输送到涉及赋值的表达式中的行为并非如此。

相反,请尝试:

test=$(echo "hello world"); echo test=$test
Run Code Online (Sandbox Code Playgroud)