Bash脚本,从stdinpipe道读取值
我试图让bash来处理数据从标准input获取pipe道,但没有运气,我的意思是没有以下工作:
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"
周围放置“”引号,也不起作用。
使用
IFS= read var << EOF $(foo) EOF
你可以把read
变成像这样的pipe道接受:
echo "hello world" | { read test; echo test=$test; }
甚至写一个这样的函数:
read_from_pipe() { read "$@" <&0; }
但没有意义 – 你的variables分配可能不会持续! 一个pipe道可能产生一个子shell,其中的环境是由值inheritance的,而不是通过引用。 这就是为什么read
不会打扰来自pipe道的input – 这是未定义的。
仅供参考, http://www.etalabs.net/sh_tricks.html是一个非常漂亮的集合,用于打击Bourne shell的怪异和不兼容性。
如果你想读大量的数据,并分别在每一行工作,你可以使用这样的东西:
cat myFile | while read x ; do echo $x ; done
如果你想将这些行分成多个单词,你可以使用多个variables来代替这个x:
cat myFile | while read xy ; do echo $y $x ; done
或者:
while read xy ; do echo $y $x ; done < myFile
但是一旦你开始想要做这样的事情真的很聪明,你最好去像Perl一样的脚本语言,你可以尝试这样的事情:
perl -ane 'print "$F[0]\n"' < myFile
用perl(或者我猜这些语言中的任何一种)的学习曲线相当陡峭,但是如果你想做最简单的脚本,你会发现从长远来看,它会更容易。 我推荐Perl Cookbook,当然还有Larry Wall等人的Perl编程语言。
read
不会从pipe道读取(或者可能由于pipe道创build子shell而丢失结果)。 但是,您可以在Bash中使用这个string:
$ read abc <<< $(echo 1 2 3) $ echo $a $b $c 1 2 3
这是另一种select
$ read test < <(echo hello world) $ echo $test hello world
我不是Bash的专家,但我想知道为什么没有提出:
stdin=$(cat) echo "$stdin"
单线程的certificate,它适用于我:
$ fortune | eval 'stdin=$(cat); echo "$stdin"'
bash
4.2引入了lastpipe
选项,它允许你的代码按照写入的方式工作,通过在当前shell的pipe道中执行最后一个命令,而不是一个子shell。
shopt -s lastpipe echo "hello world" | read test; echo test=$test
从shell命令到bashvariables的隐式pipe道的语法是
var=$(command)
要么
var=`command`
在你的例子中,你正在将数据传递给一个赋值语句,这个赋值语句没有期望任何input。
第一次尝试非常接近。 这种变化应该工作:
echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };
输出是:
testing=你好世界
你需要在pipe道后面加上括号,以便将testing任务和回声封装起来。
如果没有大括号,要testing的分配(在pipe道之后)在一个shell中,echo“test = $ test”位于不知道该分配的单独shell中。 这就是为什么你在输出中获得“test =”而不是“test = hello world”的原因。
在我眼中,从bash中读取stdin的最好方法是下面这个,它也可以让你在input结束之前在行上工作:
while read LINE; do echo $LINE done < /dev/stdin
将某些东西转换为涉及赋值的expression式并不像那样。
相反,请尝试:
test=$(echo "hello world"); echo test=$test
以下代码:
echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )
也会起作用,但是会在pipe道后面打开另一个新的子shell
echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }
惯于。
我不得不禁用作业控制来使用chepnars的方法(我从terminal运行这个命令):
set +m;shopt -s lastpipe echo "hello world" | read test; echo test=$test echo "hello world" | test="$(</dev/stdin)"; echo test=$test
Bash手册说 :
lastpipe
如果设置,并且作业控制未激活 ,则shell将运行当前shell环境中未在后台执行的pipe道的最后一个命令。
注意:默认情况下,非交互式shell中的作业控制是closures的,因此您不需要脚本中的set +m
。
我想你正在试图写一个可以从stdininput的shell脚本。 但是当你试图把它内联的时候,你试图创buildtest =variables就会迷路了。 我认为这样做是没有道理的,这就是为什么它不能像你期望的那样工作。
我试图减less
$( ... | head -n $X | tail -n 1 )
从各种input中获得特定的线路。 所以我可以键入…
cat program_file.c | line 34
所以我需要一个能够从stdin读取的小型shell程序。 像你所做地。
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 ~ $
你去了
这个怎么样:
echo "hello world" | echo test=$(cat)
这是另一种解决scheme,当读取多个variables时,以及当读入variables的数量多于进入pipe道的字数时,也可以使用这种解决scheme。
eval $(echo hickery dickery dock the mouse | { read ABC ; echo A=\"${A}\" B=\"${B}\" C=\"${C}\" ; }) echo $A hickery echo $B dickery echo $C dock the mouse
因为我爱上它,所以我想放下一张纸条。 我发现这个线程,因为我必须重写一个旧的sh脚本是POSIX兼容的。 这基本上意味着通过重写这样的代码来规避POSIX引入的pipe道/子shell问题:
some_command | read abc
成:
read abc << EOF $(some_command) EOF
和这样的代码:
some_command | while read abc; do # something done
成:
while read abc; do # something done << EOF $(some_command) EOF
但后者在空input上performance不一样。 用旧的表示法,while循环没有被input到空input上,但是在POSIX表示法中它是! 我认为这是由于EOF之前的换行符,这是不能被遗漏的。 performance得更像旧符号的POSIX代码如下所示:
while read abc; do case $a in ("") break; esac # something done << EOF $(some_command) EOF
在大多数情况下,这应该足够好。 但不幸的是,如果some_command打印一个空行,这仍然不像旧的表示法。 在旧的记号中,身体被执行,而在POSIX记号中,我们在身体的前面打破。
解决这个问题的方法可能是这样的:
while read abc; 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