sta*_*fry 46 shell pipe posix process-substitution
一些 shell,如bash
,支持进程替换,这是一种将进程输出呈现为文件的方法,如下所示:
$ diff <(sort file1) <(sort file2)
Run Code Online (Sandbox Code Playgroud)
但是,此构造不是POSIX,因此不可移植。如何处理替代的实现POSIX -友好的方式(即其中一个在工作/bin/sh
)?
注意:问题不是问如何区分两个排序的文件——这只是一个人为的例子来演示进程替换!
Sté*_*las 32
该功能是由ksh
(首次记录在 ksh86 中)引入的,并且正在使用该功能(之前在某些 BSD 和 AT&T 系统中独立添加)。在ksh93u 及更高版本中,除非您的系统支持.?zsh、bash和更高版本,否则它不会工作,并且可以在不可用的情况下使用临时命名管道(系统 III 中添加的命名管道)。/dev/fd/n
ksh
/dev/fd/n
ksh93u+
/dev/fd/n
在可用的系统上(POSIX 未指定这些),您可以自己进行进程替换(例如,):/dev/fd/n
diff <(cmd1) <(cmd2)
{
cmd1 4<&- | {
# in here fd 3 points to the reading end of the pipe
# from cmd1, while fd 0 has been restored from the original
# stdin (saved on fd 4, now closed as no longer needed)
cmd2 3<&- | diff /dev/fd/3 -
} 3<&0 <&4 4<&- # restore the original stdin for cmd2
} 4<&0 # save a copy of stdin for cmd2
Run Code Online (Sandbox Code Playgroud)
然而,这ksh93
在 Linux 上不起作用,因为 shell 管道是用 socketpairs 而不是管道实现的,并且打开/dev/fd/3
fd 3 指向套接字的位置在 Linux 上不起作用。
尽管 POSIX 没有指定,但它确实指定了命名管道。命名管道的工作方式与普通管道类似,只是您可以从文件系统访问它们。这里的问题是你必须创建临时文件并在之后清理,这很难可靠地做到,特别是考虑到 POSIX 没有标准机制(如某些系统上的 a )来创建临时文件或目录,以及信号处理(在挂断或杀死时清理)也很难便携。/dev/fd/n
mktemp -d
你可以这样做:
tmpfifo() (
n=0
until
fifo=$1.$$.$n
mkfifo -m 600 -- "$fifo" 2> /dev/null
do
n=$((n + 1))
# give up after 20 attempts as it could be a permanent condition
# that prevents us from creating fifos. You'd need to raise that
# limit if you intend to create (and use at the same time)
# more than 20 fifos in your script
[ "$n" -lt 20 ] || exit 1
done
printf '%s\n' "$fifo"
)
cleanup() { rm -f -- "$fifo"; }
fifo=$(tmpfifo /tmp/fifo) || exit
cmd2 > "$fifo" & cmd1 | diff - "$fifo"
cleanup
Run Code Online (Sandbox Code Playgroud)
(这里不处理信号处理)。
您可以依靠heredoc和/dev/fd/n文件来实现这一点。例如,bash
您可以这样做:
#!/usr/bin/env bash
paste <(echo "$SHELL") <(echo "$TERM") <(echo "$LANG")
Run Code Online (Sandbox Code Playgroud)
但在 POSIX sh 中,你必须这样做:
#!/bin/sh
paste /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF /dev/fd/5 5<<-EOF
$SHELL
EOF
$TERM
EOF
$LANG
EOF
Run Code Online (Sandbox Code Playgroud)
输出是相同的。
为了回答这个问题,这是有效的:
#!/bin/sh
diff /dev/fd/3 3<<-EOF /dev/fd/4 4<<-EOF
$(sort file1)
EOF
$(sort file2)
EOF
Run Code Online (Sandbox Code Playgroud)
它看起来很笨重,但是很有效。
如果需要避免丢失有用的变量cmd | while read A B C
,而不是:
VAR="before"
while read A B C
do
VAR="$A $VAR"
done < <(cmd)
echo "$VAR"
Run Code Online (Sandbox Code Playgroud)
您可以使用:
VAR="before"
while read A B C
do
VAR="$A $VAR"
done << EndOfText
`cmd`
EndOfText
echo "$VAR"
Run Code Online (Sandbox Code Playgroud)
所以回答这个问题:
sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6321 次 |
最近记录: |