I recently came across this really neat syntax, in the context of creating bash
functions which can accept an argument or a stream from STDIN (i.e. can be piped to). On the surface, I understand what's happening here, but I'd like a bit more explanation of the actual mechanics of how this is working.
Here's the syntax (as per the title): ${1:-$(</dev/stdin)}
And in context, one might use it as:
log(){
echo -e >&1 "INFO: ${1:-$(</dev/stdin)}"
}
Allowing the following usage:
$ log foo
INFO: foo
Alternatively, you could also do this
mv -v foo.ext bar.ext | log
INFO: renamed 'foo.ext' -> 'bar.ext'
This is great because its the single most concise method I've seen for enabling both argument and pipe functionality with bash
functions (I forget where I came across it now, unfortunately).
Now, I understand (or think I understand), most of what's happening here at least superficially, but I'd appreciate a deeper understanding. Here's how I interpret it, followed by my remaining questions:
${1:-$(</dev/stdin)}
${1}
is obviously the default argument that a function accepts${1:-x}
is a variable/brace expansion 'fall back' to the string 'x'
if $1
is otherwise empty (or unset?). In this case falling back to the STDIN process sub.$()
is obviously a </dev/stdin
is obviously a redirect from standard input which allows the pipe to work at all.This essentially says if $1
isn't populated by an argument, fall back to using STDIN - which I'm happy with conceptually.
So here are my questions:
<
) inside a $(cat < somefile.ext)
). So what is actually happening (the nitty-gritty) when the ${1:-$(</dev/stdin)}This essentially says if $1 isn't populated by an argument, fall back to using STDIN - which I'm happy with conceptually. So here are my questions: I've never seen a redirect ( < ) inside a process command substitution, without an actual command preceding it (e.g. $(cat < somefile.
In Linux, stdin is the standard input stream. This accepts text as its input. Text output from the command to the shell is delivered via the stdout (standard out) stream. Error messages from the command are sent through the stderr (standard error) stream.
The output echo generates is written in the stdout stream. Then, the piping redirects the content of stdout to stdin for the grep command. That's how grep knows what content to perform the operation on. If you want to pipe both the stderr and stdout to the next command, then use the “|&” instead.
$(
..)
: from bash manual is a command substitution not process substitution <(
..)
. and from Command substitution
The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).
/dev/stdin
is a symlink to /proc/self/fd/0
, convenient here because of $(<
..)
syntax which expects a file.
this could cause a problem because the command may be blocked until stdin closed. it is safe in the meaning that multiline input will be preserved because au double-quotes.
Finally creating a pipe and forking a process (like in mv -v foo.ext bar.ext | log
) for each log command may be inefficient.
Answering your questions
<(cmd)
and simple shell re-direction < file
. In plain form, <
basically is to read from the standard input and >
to write to standard output. The syntax < file
is a short-hand syntax to put the contents of the file
made available on the standard input which can be read by the commands. cat < file
, it basically puts the content of the file
in standard input file descriptor which is later than read
by the cat
process. The advantage of using $(<file)
would be the shell doesn't have fork an external process cat
and just use its own mechanism to read the file content.$(..)
is a syntax for command-substitution where command is run in a sub-shell environment and $(..)
is replaced with the standard output of the command. For a special case of "$(<file)"
i.e. without any commands and only re-directions involved, the shell instead of reading from the standard input starts reading from the start of the file and puts the result on the standard output.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With