我知道最简单的<
是一个接受文件输入的命令
grep search-word <filename
grep search-word < filename
,在这种情况下空格并不重要。
但在这两种情况下,只有一种语法有效,在其他地方放置空格不起作用:
(gdb) r < <(python -c "print('\x44\x43\x42\x41')")
作品;
(gdb) r << (python -c "print('\x44\x43\x42\x41')")
不起作用;
(gdb) r<<(python -c "print('\x44\x43\x42\x41')")
不起作用;
或这里
sshpass -f <(printf '%s\n' $PASSWORD)
作品;
sshpass -f < (printf '%s\n' $PASSWORD)
不起作用;
sshpass -f<(printf '%s\n' $PASSWORD)
不起作用;
这是为什么?有人能像我 5 岁一样解释一下吗?谢谢。
答案1
<
、<<
和三个事物<(
是不同的运算符。运算符内不能有空格,但在某些情况下,它们之间需要空格以帮助 shell 正确识别您的含义。
词法分析器的通常行为是吃掉产生有效运算符的最长字符集,然后将表示该运算符的标记返回给解析器,解析器然后处理该语言的实际语法。
例如:
cat < filename
生成三个标记“单词cat
”、“输入重定向运算符”、“单词filename
”。cat << EOF
产生“单词cat
”、“heredoc 运算符”、“单词EOF
”,以及cat < < EOF
会产生四个标记“单词cat
”、“输入重定向运算符”、“输入重定向运算符”、“单词EOF
”,但这在语法上没有意义,并且会产生错误。cat<filename
其行为与第一个相同,因为<
除非引用,否则该字符不能成为单词的一部分,因此标记会在此处中断。
类似地,<(
是进程替换的开始,但< (
只是两个运算符<
,并且(
因为空格将它们分开。并且,由于最长前缀匹配,<<(
here-doc 运算<<
符后跟(
and不是重定向运算符<
后跟进程替换的开始,尽管这可能是更有用的解释。
采用最长前缀的行为在其他语言中也很常见。例如,在 C 中,与(或)i+++a
相同,对于,您需要第一个 后面的空格。两者都是有效的表达方式,只是含义不同。在 C++ 中,长期以来,您必须将嵌套模板编写为,因为如果没有空格,则会被视为不相关的右移运算符,而不是模板语法所需的两个 。i ++ + a
i++ + a
i + ++a
+
vector<vector<int> >
>>
>
也就是说,你的最后一个命令,sshpass -f<(printf '%s\n' $PASSWORD)
应该工作,与 一样cat<filename
,而且,实际上它对我有用:
$ sshpass -f<(echo password) ssh foo@localhost 'echo hi'
hi
我们set -x
看到实际运行的命令是
sshpass -f/dev/fd/63 ssh foo@localhost 'echo hi'
即用文件名替换进程替换,然后才能sshpass
看到它。对于某些程序,-f foo
和之间可能存在差异-ffoo
,但这与 shell 运算符的解析方式不同。