Reputation: 12238
Why does cat exit a shell script, but only when it's fed by a pipe?
Case in point, take this shell script called "foobar.sh":
#! /bin/sh
echo $#
echo $@
cat $1
sed -e 's|foo|bar|g' $1
And a text file called "foo.txt" which contains only one line:
foo
Now if I type ./foobar.sh foo.txt
on the command line, then I'll get this expected output:
1
foo.txt
foo
bar
However if I type cat foo.txt | ./foobar.sh
then surprisingly I only get this output:
0
foo
I don't understand. If the number of arguments reported by $#
is zero, then how can cat $1
still return foo
? And, that being the case, why doesn't sed -e 's|foo|bar|g' $1
return anything since clearly $1 is foo
?
This seems an awful lot like a bug, but I'm assuming it's magic instead. Please explain!
UPDATE
Based on the given answer, the following script gives the expected output, assuming a one-line foo.txt:
#! /bin/sh
if [ $# ]
then
yay=$(cat $1)
else
read yay
fi
echo $yay | cat
echo $yay | sed -e 's|foo|bar|g'
Upvotes: 1
Views: 137
Reputation: 4274
No, $1 is not "foo". $1 is
ie, undefined/nothing.
Unlike a programming language, variables in the shell are quite dumbly and literally replaced, and the resulting commands textually executed (well, sorta kinda). In this case, "cat $1" becomes just "cat ", which will take input from stdin. That's terribly convenient to your execution since you've kindly provided "foo" on stdin via your pipe!
See what's happening?
sed likewise will read from stdin, but is already on end of stream, so exits.
Upvotes: 3
Reputation: 361899
When you don't give an argument to cat
, it reads from stdin. When $1
isn't given the cat $1
is the same as a simple cat
, which reads the text you piped in (cat foo.txt
).
Then the sed
command runs, and same as cat
, it reads from stdin because it has no filename argument. cat
has already consumed all of stdin. There's nothing left to read, so sed
quits without printing anything.
Upvotes: 2