bash中<<,<<<和<<之间有什么区别?
bash中<<
, <<<
和<<
之间有什么区别?
这里的文件
<<
被称为here-document
结构。 您让程序知道结尾文本是什么,每当看到该分隔符时,程序将读取您作为输入提供给程序的所有内容并在其上执行任务。
这就是我的意思:
$ wc << EOF > one two three > four five > EOF 2 5 24
在这个例子中,我们告诉wc
程序等待EOF
字符串,然后键入五个单词,然后键入EOF
以表示我们已完成输入。 实际上,它类似于wc
运行wc
,输入单词,然后按Ctrl D.
在bash中,这些是通过临时文件实现的,通常采用/tmp/sh-thd.
forms,而在dash中,它们是作为匿名管道实现的。 这可以通过使用strace
命令跟踪系统调用来观察。 用sh
替换bash
以查看/bin/sh
如何执行此重定向。
$ strace -e open,dup2,pipe,write -f bash -c 'cat < test > EOF'
这里字符串
<<<
被称为here-string
。 您可以为程序提供预先制作的文本字符串,而不是键入文本。 例如,使用bc
这样的程序我们可以做bc <<< 5*4
来获得该特定情况的输出,不需要以交互方式运行bc。
这里bash中的字符串是通过临时文件实现的,通常采用格式为/tmp/sh-thd.
,后来取消链接,从而使它们暂时占用一些内存空间但不显示在/tmp
列表中目录条目,并且有效地作为匿名文件存在,它们仍然可以由shell本身通过文件描述符引用,并且该文件描述符由命令inheritance,然后通过dup2()
函数复制到文件描述符0(stdin)。 这可以通过观察
$ ls -l /proc/self/fd/ <<< "TEST" total 0 lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted) lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4 lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4 lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
并通过跟踪系统调用(为了可读性而缩短输出;注意临时文件如何打开为fd 3,写入数据,然后将其重新打开,使用O_RDONLY
标志为fd 4,稍后取消链接,然后将dup2()
重新打开到fd 0,后来被cat
inheritance):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"' execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0 ... strace: Process 10229 attached [pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3 [pid 10229] write(3, "TEST", 4) = 4 [pid 10229] write(3, "\n", 1) = 1 [pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4 [pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0 [pid 10229] dup2(4, 0) = 0 [pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0 ... [pid 10229] read(0, "TEST\n", 131072) = 5 [pid 10229] write(1, "TEST\n", 5TEST ) = 5 [pid 10229] read(0, "", 131072) = 0 [pid 10229] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++
意见:可能因为这里的字符串使用了临时文本文件,因此here-strings总是插入一个尾随换行符的可能原因,因为POSIX定义的文本文件必须包含以换行符结尾的行。
流程替代
正如tldp.org解释的那样,
进程替换将进程(或进程)的输出提供给另一进程的stdin。
所以实际上这类似于将一个命令的管道标准输出到另一个命令,例如echo foobar barfoo | wc
echo foobar barfoo | wc
。 但请注意:在bash手册页中,您将看到它被表示为<(list)
。 所以基本上你可以重定向多个(!)命令的输出。
注意:从技术上讲,当你说< <
你不是指一件事,而是两个重定向与单<
和流程重定向<( . . .)
。
现在如果我们只是处理替换会发生什么?
$ echo <(echo bar) /dev/fd/63
如您所见,shell创建临时文件描述符/dev/fd/63
,其中输出(根据Gilles的答案 ,是匿名管道)。 这意味着<
将该文件描述符重定向为命令的输入。
因此,非常简单的示例是将两个echo命令的输出进程替换为wc:
$ wc < <(echo bar;echo foo) 2 2 8
所以这里我们让shell为括号中发生的所有输出创建一个文件描述符,并将其作为输入重定向到wc
。正如所料,wc从两个echo命令接收该流,这两个echo本身将输出两行,每行有一个单词,适当地,我们有2个单词,2行,6个字符加上两个新行计数。
旁注 :进程替换可以称为基础 (在高级shell中可用的命令或结构,如bash
,但不是由POSIX指定),但它是在ksh
作为ksh手册页存在之前在ksh
中实现的, 这个答案提示。 然而,像tcsh
和mksh
这样的shell没有进程替换。 那么我们怎样才能在没有进程替换的情况下将多个命令的输出重定向到另一个命令呢? 分组加管道!
$ (echo foo;echo bar) | wc 2 2 8
实际上这与上面的例子相同,但是,这与流程替换有所不同,因为我们将整个子shell的stdout和与管道链接的wc
stdin。 另一方面,进程替换使命令读取临时文件描述符。
因此,如果我们可以对管道进行分组,为什么我们需要进程替换呢? 因为有时候我们不能使用滚边。 考虑下面的例子 - 比较两个命令与diff
输出(需要两个文件,在这种情况下,我们给它两个文件描述符)
diff <(ls /bin) <(ls /usr/bin)
< <
是语法错误:
$ cat < < bash: syntax error near unexpected token `<'
< <()
是重定向( <
)的进程替换 ( <()
):
一个人为的例子:
$ wc -l < <(grep ntfs /etc/fstab) 4 $ wc -l <(grep ntfs /etc/fstab) 4 /dev/fd/63
使用进程替换,文件描述符的路径就像文件名一样使用。 如果您不想(或不能)直接使用文件名,则将进程替换与重定向结合使用。
要清楚,没有< <
操作符。
< <
是一个语法错误,你可能意味着command1 < <( command2 )
,它是一个简单的输入重定向,后跟一个进程替换,非常相似但不等同于:
command2 | command1
假设你正在运行bash
的区别是command1
在第二种情况下在子shell中运行,而它在第一种情况下在当前shell中运行。 这意味着在进程替换变体中, command1
设置的变量不会丢失。
< <
将给出语法错误。 正确使用如下:
借助示例解释:
< <()
示例:
while read line;do echo $line done< <(ls)
在上面的例子中,while循环的输入将来自ls
命令,该命令可以逐行读取并在循环中回显。
<()
用于进程替换。 有关<()
更多信息和示例,请访问此链接:
流程替换和管道