Clarification regarding behavior of shell script along with pipe
You're confusing arguments and standard input. Piping data to a program is not equivalent to giving it command line arguments.
In your first case, you are passing no arguments to your script, only feeding it data through its standard input stream. So $1
is unset for the whole duration of the script.
The first invocation of more
thus has no parameter, and pages the standard input. This displays what you had piped in there (dir1
, as text). The subsequent echo
only prints a new line since it doesn't get anything to print, and the last more
has nothing left to print either - standard input has been "drained" by the first one.
In the second case, you do pass an argument. So $1
has the value dir2
in the script. The same thing happens, except that the first more
both:
- pages through both standard input
- attempts to page the file
dir2
and errors out since that's a directory
The echo does what is expected given that $1
contains dir2
, and the last more
only errors on dir2
- it has nothing to read from standard input.
The difference is in "Arguments" VS "Standard Input".
When you run echo dir1 | bash script.sh
, the $1
argument in your script.sh
is always empty as no argument is given to it (try adding a set -x
at the begin and you will see it in the debug output).
The dir1
which is echoed comes from standard input as the more
command read stdin if no argument is given (remember $1
is empty).
How cmd1 | cmd2
works
When using pipe :
cmd2
is a subprocess ofcmd1
.- the stdin of
cmd2
is "plugged" on the stdout ofcmd1
.
As linux stdio lib offered a buffered stream through file descriptor, the stdin content will be consumed (i.e. read only once) only when stdin will be opened.
Step by step cmd1 | cmd2
workflow
Example command :
echo dir1 | (echo "a" ; read stdinvalue; echo "$stdinvalue")
echo dir1 |
: write "dir1\n
" on stdout of the first command which is not echoed but buffered through stdio and available to subprocess via stdin.echo "a"
: write "a\n
" on stdout ; doesn't read stdin ! so the "dir1\n
" string is still availableread stdinvalue
: read stdin until EOL (or EOF) and stores the string in a bash variableecho "$stdinvalue"
: write stdinvalue variable value to stdout