Well (exit),
The exit utility shall cause the shell to exit from
its current execution environment [...]
and (2.12. Shell Execution Environment)
A subshell environment shall be created as a duplicate of the shell
environment, [...] Additionally, each command of a
multi-command pipeline is in a subshell environment;
as an extension,
however, any or all commands in a pipeline may be executed in the
current environment. All other commands shall be executed in the
current shell environment.
So the exit
in a pipeline runs in an execution environment/subshell of its own, and exits only that, while the one in the simple command exit > /dev/null
runs in the main shell environment. (As noted in the comments, the redirection doesn't really affect it at all.)
Note that the part in the middle of the second quote means that some shell might run all the commands of a pipeline in the main environment, thus exiting the whole shell even in that case. In practice, that's more commonly done for the last command of a pipeline.
In Bash with lastpipe
, for example:
$ bash -c 'true | exit; echo end.'
end.
But
$ bash -O lastpipe -c 'true | exit; echo end.'
doesn't print anything.
That's a limitation of the way in which the shell breaks lines into tokens.
The shell reads lines from the input file and According to section 2 "Shell Introduction" converts them to either a word or an operator:
- The shell breaks the input into tokens: words and operators
{ is a reserved word
Some words are reserved words
Reserved words are words that have special meaning to the shell. The following words shall be recognized as reserved words:
! { } case do done elif else esac fi for if in then until while
Words, to be recognized as words, must be delimited.
Reserved words are recognized only when they are delimited ...
Mostly by blanks (point 7) and by operators.
- If the current character is an unquoted <blank>, any token containing the previous character is delimited and the current character shall be discarded.
( is an operator
Operators stand by themselves:
whereas operators are themselves delimiters.
Where "operators" are either:
3.260 Operator
In the shell command language, either a control operator or a redirection operator.
Redirection operators are:
Redirection Operator
In the shell command language, a token that performs a redirection function. It is one of the following symbols:
< > >| << >> <& >& <<- <>
Control operators are:
3.113 Control Operator
In the shell command language, a token that performs a control function. It is one of the following symbols:
& && ( ) ; ;; newline | ||
Conclusion
So, '(' and ')' are control operators while '{' '}' are reserved words.
And the exact same description of your question is inside the spec:
For instance, '(' and ')' are control operators, so that no <space> is needed in (list). However, '{' and '}' are reserved words in { list;}, so that in this case the leading <space> and <semicolon> are required.
Which exactly explains why an space (or some other delimiter) is required after a {
.
This is valid:
{ echo yes;}
As is this:
{(echo yes);}
This:
{(echo yes)}
Or even this:
{>/dev/tty echo yes;}
Best Answer
The simple answer is that
wc -l <<< "${string_variable}"
is a ksh/bash/zsh shortcut forprintf "%s\n" "${string_variable}" | wc -l
.There are actually differences in the way
<<<
and a pipe work:<<<
creates a temporary file that is passed as input to the command, whereas|
creates a pipe. In bash and pdksh/mksh (but not in ksh93 or zsh), the command on right-hand side of the pipe runs in a subshell. But these differences don't matter in this particular case.Note that in terms of counting lines, this assumes that the variable is not empty and does not end with a newline. Not ending with a newline is the case when the variable is the result of a command substitution, so you'll get the right result in most cases, but you'll get 1 for the empty string.
There are two differences between
var=$(somecommand); wc -l <<<"$var"
andsomecommand | wc -l
: using a command substitution and a temporary variable strips away blank lines at the end, forgets whether the last line of output ended in a newline or not (it always does if the command outputs a valid nonempty text file), and overcounts by one if the output is empty. If you want to both preserve the result and count lines, you can do it by appending some known text and stripping it off at the end: