嵌套进程替换后跟管道:“错误文件描述符”

嵌套进程替换后跟管道:“错误文件描述符”

我遇到了一个令人困惑的错误,我想更好地理解它。这个问题似乎需要存在一个“包装器”shell 函数(如下所述),所以我的直接兴趣是找出如何修改这样的 shell 函数以消除错误。 (我在帖子末尾对我的问题给出了更具体的陈述。)

我想出的重现此错误的最简单的代码在以下脚本中给出。 (这个脚本当然是人为的和愚蠢的,但是错误第一次出现的现实情况对于像这样的演示来说有点太复杂了。)

# create an input file
cat <<EOF > demo.txt
a
b
c
EOF

# create a "wrapper shell function" for /usr/bin/join
my_join_fn () {
  /usr/bin/join "$@"
}
cat <(my_join_fn  <(cat demo.txt) <(cat demo.txt))
cat <(my_join_fn  <(cat demo.txt) <(cat demo.txt)) | head -1

# create a "wrapper shell function" for /usr/local/bin/gjoin, a port of
# GNU's join function for OS X
my_gjoin_fn () {
  /usr/local/bin/gjoin "$@"
}
cat <(my_gjoin_fn <(cat demo.txt) <(cat demo.txt))
cat <(my_gjoin_fn <(cat demo.txt) <(cat demo.txt)) | head -1

# show the version of zsh
$SHELL --version

如果有人源此脚本(在zsh),它会成功终止,并产生以下(正确的)输出:

% source demo.sh
a
b
c
a
a
b
c
a
zsh 5.0.2 (x86_64-apple-darwin11.4.2)

但如果随后重新执行直接从命令行脚本中以 , 结尾的两行之一| head -1会出现bad file descriptor错误:

% cat <(my_join_fn  <(cat demo.txt) <(cat demo.txt)) | head -1
join: /dev/fd/11: Bad file descriptor
% cat <(my_gjoin_fn <(cat demo.txt) <(cat demo.txt)) | head -1
/usr/local/bin/gjoin: /dev/fd/11: Bad file descriptor

这是脚本中直接在命令行上运行时产生错误的唯一两行。

正如 的输出所示$SHELL --version,上面显示的结果是在 OS X 下获得的,但是当我在 Linux 下执行类似测试时,我得到了类似的结果:

% cat <(my_join_fn  <(cat demo.txt) <(cat demo.txt)) | head -1
/usr/bin/join: /proc/self/fd/11: No such file or directory
% $SHELL --version
zsh 4.3.10 (x86_64-unknown-linux-gnu)

我无法在bash(OS X 或 Linux)下重现此错误。这让我怀疑该错误是由于zsh.但是,如果是这样,这就是一个极其神秘的错误,因此不太可能很快得到修复。

因此,我想找到一个解决方法。我的问题是:

我应该如何修改包装外壳函数的定义my_gjoin_fn以避免这个错误?

(现实生活中 的对应部分my_gjoin_fn几乎与上面给出的相同,唯一不同的是在调用 时包含一个标志gjoin

my_gjoin_fn () {
  /usr/local/bin/gjoin -t$'\t' "$@"
}

我使用这个包装外壳函数每时每刻,因此我真的很想“挽救”它。)

编辑:

| head -1即使我将命令末尾的 替换为| head -10, | cat, | tee /dev/null,等,错误仍然存​​在。| :例如:

% cat <(my_join_fn  <(cat demo.txt) <(cat demo.txt)) | cat
/usr/bin/join: /proc/self/fd/11: No such file or directory

另外,ls -l /proc/self/fd按照 msw 的建议添加 ,会产生以下结果:

% cat <(ls -l /proc/self/fd; my_join_fn  <(cat demo.txt) <(cat demo.txt)) | cat
total 0
lrwx------ 1 jones jones 64 Aug 21 12:29 0 -> /dev/pts/18
l-wx------ 1 jones jones 64 Aug 21 12:29 1 -> pipe:[312539706]
lrwx------ 1 jones jones 64 Aug 21 12:29 2 -> /dev/pts/18
lr-x------ 1 jones jones 64 Aug 21 12:29 3 -> /proc/23849/fd
/usr/bin/join: /proc/self/fd/11: No such file or directory

……这并不能说明什么很多,但可能为其他人提供更多信息。 FWIW,ls -l /proc/self/fd无论我在zsh还是在bash.另外,FWIW,ls -l /proc/self/fd单独运行时的输出是

% ls -l /proc/self/fd
total 0
lrwx------ 1 jones jones 64 Aug 21 12:32 0 -> /dev/pts/18
lrwx------ 1 jones jones 64 Aug 21 12:32 1 -> /dev/pts/18
lrwx------ 1 jones jones 64 Aug 21 12:32 2 -> /dev/pts/18
lr-x------ 1 jones jones 64 Aug 21 12:32 3 -> /proc/5246/fd

答案1

我能够使用 重现该行为zsh 4.3.10 (i686-pc-linux-gnu)

% cat <(funjoin  <(cat demo) <(cat demo)) | head -1 
join: /proc/self/fd/11: No such file or directory

我深入研究了手册,发现最接近这个问题的是 Process Substitution inman zshexpn和 MULTIOS in章节man zshmisc

这两章都建议了一种解决方法,包括在命令的一部分周围加上大括号。

我试过了

% { cat <(funjoin  <(cat demo) <(cat demo)) } | head -1
1

它有效。

我无法完全理解{ }zsh 中的语义。手册将其简单地解释为命令列表。我也不完全明白这个 MULTIOS 到底是做什么的。启用或禁用它似乎没有什么区别。

我尝试将大括号放在不同的位置,包括函数主体中funjoin,但它唯一可以正确工作的地方是外部cat.

答案2

我无法在 Linux 上的 zsh_5.0.0-2ubuntu3_amd64.deb 下重现这一点,但/proc/self/fd/11似乎是一个相当高的数字。它只会失败吗head -1?怎么样head -10?输出是否为

cat <(ls -l /proc/self/fd ; my_join_fn  <(cat demo.txt) <(cat demo.txt)) | head -1

产生任何照明吗?

相关内容