创建临时文件 vs 进程替换 vs 变量扩展?

mun*_*ish 5 linux process-substitution variable

如果我正在做类似的事情

  1. 创建临时文件

    some process generating output > temp_file
    cat  temp_file
    
    Run Code Online (Sandbox Code Playgroud)
  2. 过程替换:

    cat <(some process generating output)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 其它的办法 :

    cat <<<(some process generating output)
    
    Run Code Online (Sandbox Code Playgroud)

我对这些有一些疑问:

  1. 进程替换<() >()或变量扩展的 数据输出大小是否有限制<<<()
  2. 其中哪一个是最快的,或者有什么方法可以做得更快?

我的 ulimit 命令输出是:

bash-3.00$ ulimit -a
core file size        (blocks, -c) unlimited
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
open files                    (-n) 256
pipe size          (512 bytes, -p) 10
stack size            (kbytes, -s) 8480
cpu time             (seconds, -t) unlimited
max user processes            (-u) 8053
virtual memory        (kbytes, -v) unlimited
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 6

<(cmd)ksh现在也发现的一个特性zshbash称为进程替换

在支持/dev/fd/nor 的系统上/proc/self/fd/n,它是用管道实现的,而不是用临时命名管道实现的。无论如何,它是一种管道形式,是一种进程间通信机制。

cmd1 <(cmd2)
Run Code Online (Sandbox Code Playgroud)

可以写(使用普通管道):

{ cmd2 4<&- | 3<&0 <&4 4<&- cmd1 /dev/fd/3; } 4<&0
Run Code Online (Sandbox Code Playgroud)

或者(使用命名管道):

mkfifo /tmp/named_pipe
cmd2 > /tmp/named_pipe & cmd1 /tmp/named_pipe
Run Code Online (Sandbox Code Playgroud)

也就是说,两个命令同时启动并与管道通信。您通常会使用cmd2 | cmd1它,但过程替换通常用于那些cmd1只能从文件名而不是从标准输入中获取输入的情况,或者需要多个输入的情况,例如 in diff <(cmd1) <(cmd2)

除了进程数、cpu 时间或内存等一般限制外,没有影响它的限制。

所述PIPEBUF报道的一些实施ulimitbash和一些实施方式中的ksh是不是一个而是RLIMIT针对其的配管的写入被保证是原子所以在这里无关紧要的最大大小。管道本身的大小(@dsmsk80 报告的 Linux 上为 64kB)本身并不是一个真正的限制。它只是说cmd2即使在cmd1停止读取管道后也可以写入管道。

但是有一个限制,它cmd1只能从该文件中读取。因为它是一个管道,它不能写入该文件或在文件中来回查找。

zsh 具有使用常规临时文件的第三种命令替换形式:

cmd1 =(cmd2)
Run Code Online (Sandbox Code Playgroud)

cmd1使用包含输出的临时文件调用cmd2。在这种情况下cmd1cmd2之后运行而不是同时运行。在那里可能会达到文件大小的限制。

我不知道任何实现<<<(...)运算符的外壳。然而,<<<在and 的最新版本中也发现了一个操作符 in zsh(灵感来自 Unix 端口中的同一操作符rc)。这是heredoc 运算符的变体,称为herestring。ksh93bash<<

在:

cmd <<< something
Run Code Online (Sandbox Code Playgroud)

与标准相同的是:

cmd << EOF
something
EOF
Run Code Online (Sandbox Code Playgroud)

shell 创建一个临时文件something\n作为内容并将其作为标准输入提供给新进程,取消该文件的链接并cmd在该新进程中执行。同样,这是一个常规文件,因此可能会达到文件最大大小的 rlimit。

现在您可以将<<<运算符与$(...)(命令替换)结合起来以某种方式模拟和 中zsh=(...)运算符:bashksh93

cmd1 <<<"$(cmd2)"
Run Code Online (Sandbox Code Playgroud)

cmd2与它一起运行stdout 重定向到管道。在管道的另一端,shell 读取输出cmd2并存储它减去尾随的换行符,并将一个换行符添加到临时文件中,并调用cmd1打开的临时文件以作为标准输入读取(注意还有另一个限制)如果cmd2输出包含 NUL 字符,它将不起作用)。

就像=(...),你必须这样写:

cmd1 /dev/fd/3 3<<<"$(cmd3)"
Run Code Online (Sandbox Code Playgroud)

请注意,shell 必须在将 cmd3 写入临时文件之前读取内存中的整个输出,因此除了最大文件大小外,您还可能达到内存使用限制。

另请注意,从版本 5 开始,bash在调用 之前删除临时文件的写权限cmd1,因此如果您需要cmd1能够修改该文件,则需要使用以下方法解决它:

{
  chmod u+w /dev/fd/3 && # only needed in bash 5+
  cmd1 /dev/fd/3
} 3<<<"$(cmd3)"
Run Code Online (Sandbox Code Playgroud)


小智 5

如果系统支持命名管道,则以<(cmd)和形式的 Bash 进程替换>(cmd)使用命名管道实现。该命令cmd在其输入/输出连接到管道的情况下运行。当您运行 eg 时,cat <(sleep 10; ls)您可以在目录下找到创建的管道/proc/pid_of_cat/fd。然后将此命名管道作为参数传递给当前命令 ( cat)。

管道的缓冲区容量可以通过使用dd命令的巧妙用法来估计,该命令将零数据发送到sleep命令的标准输入(它什么都不做)。显然,该进程将休眠一段时间,因此缓冲区将变满:

(dd if=/dev/zero bs=1 | sleep 999) &
Run Code Online (Sandbox Code Playgroud)

稍等片刻,然后向进程发送USR1信号dd

pkill -USR1 dd
Run Code Online (Sandbox Code Playgroud)

这使得打印出 I/O 统计信息的过程:

65537+0 records in
65536+0 records out
65536 bytes (66 kB) copied, 8.62622 s, 7.6 kB/s
Run Code Online (Sandbox Code Playgroud)

在我的测试用例中,缓冲区大小是64kB( 65536B)。

你如何使用<<<(cmd)扩展?我知道这是 here 文档的一种变体,它被扩展并传递给其标准输入上的命令。

希望我对有关尺寸的问题有所了解。关于速度,我不太确定,但我认为这两种方法都可以提供相似的吞吐量。