Answers:
管道是在内核文件系统中打开的文件,不能作为常规文件在磁盘上访问。它只会自动缓冲到特定大小,并在满时最终会阻塞。与从块设备上获取的文件不同,管道的行为与字符设备非常相似,因此通常不支持,lseek()
并且从管道读取的数据无法像常规文件那样再次读取。
这里的字符串是在已挂载文件系统中创建的常规文件。Shell创建文件并保留其描述符,同时在它向文件中写入/读取一个字节之前立即删除其唯一的文件系统链接(并因此删除了它)。内核将保持文件所需的空间,直到所有进程释放该文件的所有描述符为止。如果从此类描述符读取的子级有能力这样做,则可以将其倒退lseek()
并读取。
在这两种情况下,标记<<<
和都|
代表文件描述符,而不一定代表文件本身。通过执行以下操作,您可以更好地了解正在发生的事情:
readlink /dev/fd/1 | cat
...要么...
ls -l <<<'' /dev/fd/*
这两个文件之间最显着的区别是,here-string / doc几乎是一次全部的事情-shell在向子级提供读取描述符之前将所有数据写入其中。另一方面,shell在适当的描述符上打开管道,并派生子级来管理管道的子进程-因此,它在两端同时写入/读取。
但是,这些区别通常是正确的。据我所知(实际上还不是全部),几乎所有处理shell <<<
此处字符串缩写的shell都适用<<
,唯一的例外是yash
。yash
,busybox
,dash
,和其他ash
变异体都倾向于管道回到这里的文档,虽然,所以在那些炮弹真的是两个毕竟差别很小。
好的-两个例外。现在,我正在考虑它,ksh93
实际上并没有为它做任何管道|
,而是通过套接字处理整个业务,尽管它确实<<<*
像大多数其他工具一样处理了已删除的tmp文件。而且,它仅将管道的各个部分放在subshell环境中,这是一种POSIX委婉说法,至少它的作用类似于subshell,所以甚至不做分叉。
事实是,@ PSkocik的基准测试(非常有用)在这里的结果可能因多种原因而有很大差异,其中大多数取决于实现。对于此处的文档设置,最大的因素将是目标${TMPDIR}
文件系统类型和当前缓存配置/可用性,以及要写入的数据量。对于管道,这将是Shell进程本身的大小,因为将为所需的fork创建副本。这种方式在管道设置(包括命令替换)时bash
非常糟糕 -因为它很大且很慢,但是几乎没有任何区别。$(
)
ksh93
这是另一个小shell片段,用于演示shell如何为管道拆分子shell:
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
流水线pipe_who()
调用报告与当前外壳中一次运行的报告之间的差异是由于(
子外壳)
指定的行为,即在$$
扩展时要求父外壳的pid 。尽管bash
子shell绝对是单独的进程,但是$$
特殊的shell参数并不是此信息的可靠来源。子sh
外壳的子外壳仍然不会拒绝准确报告其子外壳$PPID
。
基准测试无可替代:
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done )
real 0m2.080s
user 0m0.738s
sys 0m1.439s
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done )
real 0m4.432s
user 0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done )
real 0m3.380s
user 0m1.121s
sys 0m3.423s
对于大量数据:
TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done )
real 0m42.327s
user 0m38.591s
sys 0m4.226s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done )
real 1m26.946s
user 1m23.116s
sys 0m3.681s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done )
real 0m43.910s
user 0m40.178s
sys 0m4.119s
看起来管道版本的安装成本较高,但最终效率更高。
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/null
似乎在两种情况下都很快...
echo "$longstring"
或<<<"$longstring"
将进行调整以提高效率,并与短字符串,效率没有多大意义呢。
cat <(echo foo) >/dev/null
比echo foo | cat >/dev/null
。