我见过 Fish shell 将进程替换作为函数实现:
# === Fish shell lang:
function psub
mkfifo $the_pipe
cat >$the_pipe &
echo $the_pipe
# remove pipe when bg job is done
end
# Example:
diff (echo abc | psub) (echo def | psub)
完整代码:https://github.com/fish-shell/fish-shell/blob/master/share/functions/psub.fish
我已经尝试了几个小时来为非 Fish shell (mksh) 重新实现它,但无法做到这一点:
# Executable file: my.psub.sh
the_pipe="$(mktemp /tmp/pipe.XXXXXXXXXX)"
mkfifo $the_pipe
cat >$the_pipe &
echo $the_pipe
# Example:
diff $(echo abc | my.psub.sh) $(echo def | my.psub.sh)
命令块。我已经尝试了我能想到的一切,但我不知道下一步该去哪里。
答案1
这有点困难但可行:
function die {
print -ru2 -- "E: $*"
exit 1
}
function psubin {
local stdin=$(cat; echo .) pipe
pipe=$(mktemp /tmp/psub.XXXXXXXXXX) || die mktemp
# this is racy
rm -f "$pipe"
mkfifo "$pipe" || die mkfifo
(
# don’t block parent
exec <&- >&- 2>&-
# write content to FIFO
print -nr -- "${stdin%.}" >"$pipe"
# signal EOF to reader, ensure it’s not merged
sleep 0.1
:>"$pipe"
# clean up
(sleep 1; rm -f "$pipe") &
) &
print -nr -- "$pipe"
}
diff -u $(echo abc | psubin) $(echo def | psubin)
我和你在这里遇到的问题是:
mkfifo
除非你rm
mktemp
先输出否则会抱怨- 如果 mksh 仍然与父进程共享任何文件描述符(stdin、stdout、stderr),则 mksh 会阻止后台进程(注意:这可能是使用
>&-
而不是 的唯一有效用例>/dev/null
,并且只是因为我们可以保证这些 fd 不再被使用,也不被任何新的 fd 取代) - 因为我们在后台进程中没有标准输入,所以我们需要缓存其内容,字节精确
- 带有 FIFO(命名管道)的 EOF 并非易事。让我们就这样吧……(我们可以尝试一些技巧,尝试以非阻塞方式打开 FIFO 以检查是否有读取器,并发出 EOF 信号直到读取器死亡,但这已经足够好了目前)
- 如果使用后将 FIFO 移除就更好了……
另一方面:我们现在这样做是件好事,因为我最终希望将其实现<(…)
到 mksh 本身中,然后我需要知道要注意什么,因为我们不能/dev/fd/
像 GNU bash 那样使用,因为那是不够便携。
答案2
这似乎不可能:
diff (echo abc | ./my.psub.sh) (echo def | ./my.psub.sh)
它会阻塞,因为 2 个子 shell 需要在diff
运行之前退出。
它在 Fish shell 上是可行的,但很可能是因为它们执行命令和进程替换等操作非常不同和这是它自己的一系列问题。
另一种选择是busybox
按照“mksh 中进程替换的解决方法”:
busybox diff =(sort ./a) =(sort ./b)