以下代码描述了我的问题。
# 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:
由于管道创建了一个子shell,这意味着当我们返回work()
时,f()
中定义的变量会丢失,我以这种方式修复了问题。
# 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
被覆盖时,它会导致问题(可解决的问题)当caller-f()仍然依赖它时,调用-f()。当然,可以通过充分注意out.txt
的使用来解决它,即仅在f()调用自身之前使用out.txt
并避免在调用之下使用out.txt
。
这让我想知道是否有任何方法可以在不依赖于创建临时文件的情况下解决此问题。有没有办法将work
的输出传递给另一个命令而不必求助于文件I / O,以便work
在当前shell中执行?我正在寻找可以在任何POSIX shell上运行的解决方案。
答案 0 :(得分:2)
kludge(在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
保留在同一个shell中。与函数f
中的上述代码相同:
f() { { { work ; echo a: $a 1>&3 ; } | head -n 2 ; } 3>&1 }
答案 1 :(得分:1)
我已经实现了一个函数public function userDocuments()
{
return $this->hasMany('App\Models\Document');
}
,用于以请求的方式执行管道(即管道的第一个命令在当前shell中执行)。但请注意,实现会创建临时文件,因此不符合避免文件I / O的要求。
<强>用法:强>
以下任何形式和其他变体都有效:
pseudopipe
当前实施的限制:
只有正确处理由两个命令组成的管道
这些命令不得包含带引号或转义的&#39; pseudopipe "cmd1|cmd2"
pseudopipe 'cmd1|cmd2'
pseudopipe cmd1\|cmd2
&#39;字符。例如,以下内容无法按预期工作:
|
您的脚本不得使用保留变量 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
答案 2 :(得分:1)
问题是您需要来自子shell的多条信息。一种解决方案是将您想要的所有内容从子shell放入标准输出管道并将其全部处理回父shell端:
work()
{
a=foo
echo $a
ps -ef
}
然后你要在父shell端做一些解析工作。在这里,我将分隔符设置为换行符,并将返回字符串转换为每个数组索引处有一行的数组:
f()
{
output=$(work | head -n 3)
IFS=$'\n'
lines=($output)
a=${lines[0]}
echo ${lines[1]}
echo ${lines[2]}
echo a=$a
}
f
另一个解决方案是使用命名管道并将您感兴趣的每个变量分配到自己的命名管道中。在子shell端,您可以创建和编写管道:
mkfifo /tmp/variable_a
echo $a > /tmp/variable_a
在父shell端,您从同一个命名管道中读取:
read a < /tmp/variable_a
rm -f /tmp/variable_a