bash中<<,<<和<<之间有什么区别?


102

什么之间的区别<<<<<< <在bash?


20
至少在这些以Google为中心的时代,很难搜索这些基于符号的运算符。是否有一个搜索引擎,您可以在其中插入“ << <<< <<”并获得有用的信息?
Daniel Griscom 2015年

11
@DanielGriscom有SymbolHound
丹尼斯

1
@DanielGriscom Stack Exchange曾经支持搜索符号,但是后来出现了问题,没有人修复它。
muru

它已经存在(已经使用了将近一年):Shell的控制和重定向运算符是什么?
斯科特

Answers:


115

这里文件

<<被称为here-document结构。您让程序知道结束文本是什么,每当看到分隔符时,程序就会读取您提供给程序的所有内容作为输入并在其上执行任务。

这就是我的意思:

$ wc << EOF
> one two three
> four five
> EOF
 2  5 24

在此示例中,我们告诉wc程序等待EOF字符串,然后键入五个单词,然后键入EOF以表明我们已完成输入。实际上,它类似于单独运行wc,输入文字然后按CtrlD

在bash中,这些是通过临时文件(通常以形式)实现的/tmp/sh-thd.<random string>,而在破折号中,它们是作为匿名管道实现的。这可以通过使用strace命令跟踪系统调用来观察。替换bashsh以查看如何/bin/sh执行此重定向。

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'

这里字符串

<<<被称为here-string。无需输入文本,而是将预制的文本字符串提供给程序。例如,使用bc我们可以做bc <<< 5*4的仅获取特定情况下的输出的程序,无需交互运行bc。

bash中的这里字符串是通过临时文件实现的,这些临时文件通常采用格式/tmp/sh-thd.<random string>,以后会取消链接,从而使它们暂时占据一些内存空间,但不会显示在/tmp目录条目列表中,并有效地以匿名文件形式存在,这些文件可能仍然Shell本身通过文件描述符来引用该文件,然后由命令继承该文件描述符,然后再通过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后面):

$ 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 +++

意见:可能是因为此处的字符串使用了临时文本文件,这可能是此处字符串始终插入尾随换行符的可能原因,因为POSIX定义的文本文件必须具有以换行符结尾的行。

流程替代

正如tldp.org解释的那样,

进程替换将一个或多个进程的输出馈送到另一个进程的标准输入中。

因此实际上这类似于将一个命令的stdout传递给另一个命令,例如echo foobar barfoo | wc。但请注意:在bash联机帮助页中,您会看到它表示为<(list)。因此,基本上,您可以重定向多个(!)命令的输出。

注意:从技术上讲,您< <并不是在指一件事,而是指两个重定向(单个 <重定向)和进程对输出的重定向<( . . .)

现在,如果仅进行过程替换会发生什么?

$ echo <(echo bar)
/dev/fd/63

如您所见,shell /dev/fd/63在输出所在的位置创建了临时文件描述符(根据Gilles的回答,它是一个匿名管道)。这意味着 <将文件描述符重定向为命令的输入。

因此,非常简单的示例是将两个回显命令的输出替换为wc:

$ wc < <(echo bar;echo foo)
      2       2       8

因此,在这里我们让Shell为括号中出现的所有输出创建一个文件描述符,并将其重定向为输入到wc.wc像预期的那样,从两个echo命令接收该流,其本身将输出两行,每行包含一个单词,并适当地,我们有2个单词,2行和6个字符加上两个换行符。

边注:进程替换可被称为bashism(命令或结构,先进的炮弹一样使用bash,但不是POSIX指定),但它是在执行ksh前bash的存在作为KSH手册页这个答案建议。像tcshmksh这样的外壳没有进程替代。那么,如何在不进行进程替换的情况下将多个命令的输出重定向到另一个命令呢?分组加管道!

$ (echo foo;echo bar) | wc
      2       2       8

实际上,这与上面的示例相同,但是,这与进程替换在本质上是不同的,因为我们将整个子外壳的stdout和wc 与管道链接的 stdin 制成。另一方面,进程替换使命令读取临时文件描述符。

因此,如果我们可以使用管道进行分组,为什么我们需要流程替代?因为有时我们不能使用管道。考虑下面的示例-比较两个命令的输出与diff(需要两个文件,在这种情况下,我们给它两个文件描述符)

diff <(ls /bin) <(ls /usr/bin)

7
< <当人们从一个进程替换中获得标准输入时使用。这样的命令可能类似于:cmd1 < <(cmd2)。例如,wc < <(date)
John1024


2
< < 本身不是一件事情,在流程替换的情况下,它<后面只是一些其他的事情开始<
immibis

1
据我所知,@ muru <<<首先由Plan 9 rc shell的Unix端口实现,然后由zsh,bash和ksh93采用。我不会再称其为bashism。
jlliagre

3
哪里不能使用管道又如:echo 'foo' | read; echo ${REPLY}不会返回foo,因为read是开始一个子shell -管道启动一个子shell。但是,read < <(echo 'foo'); echo ${REPLY}正确返回foo,因为没有子外壳。
帕迪·兰道

26

< < 是语法错误:

$ cat < <
bash: syntax error near unexpected token `<'

< <()是将流程替换<())与重定向(<)结合使用:

一个人为的例子:

$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63

通过进程替换,文件描述符的路径就像文件名一样使用。如果您不想(或不能)直接使用文件名,则可以将进程替换与重定向结合使用。

需要明确的是,没有< <运算符。


我的回答是,<<(()比<()更有用,对吗?
solfish

1
@solfish <()给出了类似文件名的内容,因此它更有用- < <()在可能不需要的地方替换stdin。在中wc,后者恰好更有用。它可能在其他地方用处不大
muru

12

< <是语法错误,您可能是说command1 < <( command2 )这是一个简单的输入重定向,后跟一个进程替换,它非常相似,但不等同于:

command2 | command1

假设您正在运行的差异bashcommand1在第二种情况下在子外壳程序中运行,而在第一种情况下是在当前外壳程序中运行。这意味着设置的变量command1不会因过程替换变量而丢失。


11

< <将给出语法错误。正确使用如下:

借助示例进行解释:

示例< <()

while read line;do
   echo $line
done< <(ls)

在上面的示例中,while循环的输入来自ls命令,该命令可以逐行读取并echo在循环中进行编辑。

<()用于流程替换。有关更多信息和示例,请<()参见以下链接:

工艺替代和管道

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.