0

I know that the simple < is a command accepting input from a file grep search-word <filename grep search-word < filename and spaces don't matter in that case.

But in those 2 cases there is only ONE syntax that is working, putting spaces elsewhere is not working:

(gdb) r < <(python -c "print('\x44\x43\x42\x41')") works; (gdb) r << (python -c "print('\x44\x43\x42\x41')") doesn't work; (gdb) r<<(python -c "print('\x44\x43\x42\x41')") doesn't work;

or here

sshpass -f <(printf '%s\n' $PASSWORD) works; sshpass -f < (printf '%s\n' $PASSWORD) doesn't work; sshpass -f<(printf '%s\n' $PASSWORD) doesn't work;

Why is that? Could someone can explain like I'm 5? Thanks.

6

The three things <, << and <( are distinct operators. You can't have whitespace within an operator, but in some cases, whitespace is required between them to help the shell correctly identify what you mean.

The usual behaviour for lexers is to eat the longest set of characters that produces a valid operator, and then return the token representing that operator to the parser, which then deals with the actual syntax of the language.

E.g.:

  • cat < filename produces the three tokens "the word cat", "input redirection operator", "the word filename".

  • cat << EOF produces "the word cat", "heredoc operator", "the word EOF", and

  • cat < < EOF would produce the four tokens "the word cat", "input redirection operator", "input redirection operator", "the word EOF", but that's syntactically meaningless and gives an error.

  • cat<filename acts identically to the first one, since the < character can't be part of a word unless quoted, so the token breaks there.

Similarly, <( is the start of a process substitution, but < ( is just the two operators < and ( because the space breaks them. And, because of longest-prefix matching, <<( is the here-doc operator << followed by the ( and not the redirection operator < followed by the start of a process substitution, even though that might be a more useful interpretation.

The behaviour of taking the longest prefix is rather common in other languages too. E.g. in C, i+++a is the same as i ++ + a (or i++ + a), and for i + ++a, you need the space after the first +. Both are valid expressions, they just mean different things. And in C++, it was for long the case that you had to write nested templates as vector<vector<int> >, since without the space, the >> would be treated as the unrelated right-shift operator instead of two >'s which the template syntax requires.


That said, your last command, sshpass -f<(printf '%s\n' $PASSWORD) should work, in the same way as cat<filename, and, actually it does work for me:

$ sshpass -f<(echo password) ssh foo@localhost 'echo hi'
hi

With set -x we see the command that actually runs, is

sshpass -f/dev/fd/63 ssh foo@localhost 'echo hi'

i.e. with the process substitution replaced by a filename, before sshpass sees it. For some programs, there could be a difference between -f foo and -ffoo, though, but that's a different matter from how the shell operators are parsed.

1
  • Yes thank you! And indeed sshpass -f<(printf '%s\n' $PASSWORD) works for me as well, I maybe got an error while I was trying to debug my command and the error was elsewhere at that moment! I didn't know about longest-prefix matching! Apr 11 '21 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.