![](/img/trans.png)
[英]Shell script to pass the output of first command to the next in pipe
[英]Pass output from first command as input to second command such that the first command runs in current shell
以下代码描述了我的问题。
# File: foo.sh
# We have a work function that may define variables and produce some
# complex output.
work()
{
a=foo
ps -ef
}
# This function uses the work function.
f()
{
# We want to filter the complex output produced by work function but
# at the same time we want that any variables defined by work
# function is available in this function.
work | head -n 2
# Since pipeline launches subshells, the variable defined by work
# function is no longer available in this function.
echo a: $a
}
f
这是输出。
$ sh foo.sh
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jul15 ? 00:00:03 /sbin/init
a:
由于管道创建了一个子外壳,这意味着当我们返回f()
时, work()
中定义的变量会丢失,因此我以这种方式解决了该问题。
# File: bar.sh
# We have a work function that may define variables and produce some
# complex output.
work()
{
a=foo
ps -ef
}
# This function uses the work function.
f()
{
# We want to filter the complex output produced by work function but
# at the same time we want that any variables defined by work
# function is available in this function.
work > out.txt
head -n 2 out.txt
echo a: $a
}
f
这是输出。
$ sh bar.sh
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jul15 ? 00:00:03 /sbin/init
a: foo
尽管第二个脚本对于此玩具示例而言效果很好,但是当f()
最终以递归方式调用自身时,可能会导致问题(尽管可解决问题),这将导致out.txt
在被调用时被-f()覆盖。调用者f()仍将依赖它。 当然,也可以通过采取有关的使用充分注意要解决out.txt
,即使用out.txt
仅F()之前调用自身,并避免使用out.txt
通话下方。
这使我想知道是否有任何方法可以在不依赖于创建临时文件的情况下解决此问题。 有没有一种方法可以将work
的输出传递到另一个命令,而不必求助于文件I / O,这样work
可以在当前Shell中执行? 我正在寻找可以在任何POSIX shell上运行的解决方案。
一个kudge(在yash
测试),将变量输出到文件描述符3并绕过head
,然后将其复制回STDIN :
{ { work ; echo a: $a 1>&3 ; } | head -n 2 ; } 3>&1
输出:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jul15 ? 00:00:41 /sbin/init splash
a: foo
注意花括号的两个级别:
head
看不到它。 work
和echo
保持在同一外壳中。 与上面相同的代码,在函数f
:
f() { { { work ; echo a: $a 1>&3 ; } | head -n 2 ; } 3>&1 }
我已经实现了一个函数pseudopipe
管道,用于以请求的方式执行管道(即管道的第一个命令在当前shell中执行)。 但是请注意,该实现会创建临时文件,因此不符合避免文件I / O的要求。
用法:
以下任何形式和其他变体都可以使用:
pseudopipe "cmd1|cmd2"
pseudopipe 'cmd1|cmd2'
pseudopipe cmd1\|cmd2
当前实施的局限性:
仅正确处理由两个命令组成的管道
命令中不得包含带引号或转义的' |
字符。 例如,以下操作将无法正常工作:
pseudopipe "work|grep '|'"
您的脚本不得使用保留变量PSEUDOPIPE_TMPDIR
, PSEUDOPIPE_CMD1
和PSEUDOPIPE_CMD2
实现和示例:
#!/bin/sh
pseudopipe()
{
PSEUDOPIPE_TMPDIR=$(mktemp -d)
PSEUDOPIPE_CMD1=$(echo "$@"|sed 's/|/\n/g'|head -n 1)
PSEUDOPIPE_CMD2=$(echo "$@"|sed 's/|/\n/g'|tail -n 1)
mkfifo "$PSEUDOPIPE_TMPDIR"/fifo
eval "$PSEUDOPIPE_CMD2 < $PSEUDOPIPE_TMPDIR/fifo &" \
"$PSEUDOPIPE_CMD1 > $PSEUDOPIPE_TMPDIR/fifo ;" \
"rm -r $PSEUDOPIPE_TMPDIR"
wait $!
}
work()
{
a=foo
ps -ef
}
f()
{
a=bar
echo "before pseudopipe: a: $a"
pseudopipe "work|head -n 2"
echo "after pseudopipe: a: $a"
}
f
输出:
$ sh pseudopipe.sh
before pseudopipe: a: bar
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jul22 ? 00:00:06 /sbin/init splash
after pseudopipe: a: foo
问题是您希望从子外壳中获取多个信息。 一种解决方案是将所需的所有内容从子shell放入标准输出管道,然后全部返回父shell端:
work()
{
a=foo
echo $a
ps -ef
}
然后,您需要在父外壳程序端进行一些解析工作。 在这里,我将分隔符设置为换行符,并将返回字符串转换为每个数组索引处只有一行的数组:
f()
{
output=$(work | head -n 3)
IFS=$'\n'
lines=($output)
a=${lines[0]}
echo ${lines[1]}
echo ${lines[2]}
echo a=$a
}
f
另一个解决方案是使用命名管道,并将您感兴趣的每个变量分配到其自己的命名管道中。 在子外壳程序端,您可以创建和编写管道:
mkfifo /tmp/variable_a
echo $a > /tmp/variable_a
在父外壳程序端,您从相同的命名管道读取:
read a < /tmp/variable_a
rm -f /tmp/variable_a
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.