Reputation: 1932
I am new to bash-scripting & trying to understand how things work. It's all a bit strange..
I have two scripts. First this one:
#!/usr/bin/bash
#name: stderrtest0.sh
echo "on ${1}: this is an error" >&2
echo "on ${1}: this is an info" >&1
echo "on ${1}: this is just text"
And this second script calls the first:
#!/usr/bin/bash
#name: stderrtest1.sh
echo "invoking: stderrtest0.sh test1 >&2 ~output:"
./stderrtest0.sh test1 >&2
echo "invoking: stderrtest0.sh test2 >&1 ~output:"
./stderrtest0.sh test2 >&1
echo "invoking: stderrtest0.sh test3 2>&1 ~output:"
./stderrtest0.sh test3 2>&1
echo "invoking: stderrtest0.sh test4 1>&2 ~output:"
./stderrtest0.sh test4 1>&2
echo "invoking: stderrtest0.sh test5 ~output:"
./stderrtest0.sh test5
Here are my tests (debian squeeze) with the output:
METATEST1) invoke stderrtest1.sh
$ ./stderrtest1.sh
invoking: stderrtest0.sh test1 >&2 ~output:
on test1: this is an error
on test1: this is an info
on test1: this is just text
invoking: stderrtest0.sh test2 >&1 ~output:
on test2: this is an error
on test2: this is an info
on test2: this is just text
invoking: stderrtest0.sh test3 2>&1 ~output:
on test3: this is an error
on test3: this is an info
on test3: this is just text
invoking: stderrtest0.sh test4 1>&2 ~output:
on test4: this is an error
on test4: this is an info
on test4: this is just text
invoking: stderrtest0.sh test5 ~output:
on test5: this is an error
on test5: this is an info
on test5: this is just text
This is as I expect. Since by default stderr
& stdout
get sent to the terminal.
METATEST2) invoke stderrtest1.sh & redirect output to out
$ ./stderrtest1.sh >out
on test1: this is an error
on test1: this is an info
on test1: this is just text
on test2: this is an error
on test4: this is an error
on test4: this is an info
on test4: this is just text
on test5: this is an error
$ cat out
invoking: stderrtest0.sh test1 >&2 ~output:
invoking: stderrtest0.sh test2 >&1 ~output:
on test2: this is an info
on test2: this is just text
invoking: stderrtest0.sh test3 2>&1 ~output:
on test3: this is an error
on test3: this is an info
on test3: this is just text
invoking: stderrtest0.sh test4 1>&2 ~output:
invoking: stderrtest0.sh test5 ~output:
on test5: this is an info
on test5: this is just text
So here:
stdout
gets sent to the file out stderr
gets sent to the terminalThis is not quite as I expect. I somehow thought everything might end up in out
METATEST3) invoke stderrtest1.sh & redirect stdout
to inf.out
$ ./stderrtest1.sh 1>inf.out
on test1: this is an error
on test1: this is an info
on test1: this is just text
on test2: this is an error
on test4: this is an error
on test4: this is an info
on test4: this is just text
on test5: this is an error
$ cat inf.out
invoking: stderrtest0.sh test1 >&2 ~output:
invoking: stderrtest0.sh test2 >&1 ~output:
on test2: this is an info
on test2: this is just text
invoking: stderrtest0.sh test3 2>&1 ~output:
on test3: this is an error
on test3: this is an info
on test3: this is just text
invoking: stderrtest0.sh test4 1>&2 ~output:
invoking: stderrtest0.sh test5 ~output:
on test5: this is an info
on test5: this is just text
Results are identical to METATEST2:
stdout
gets sent to the file inf.outstderr
gets sent to the terminalOk. Now I understand METATEST2. Redirecting without specification defaults to stdout
.
METATEST4) invoke stderrtest1.sh & redirect stderr
to err.out
$ ./stderrtest1.sh 2>err.out
invoking: stderrtest0.sh test1 >&2 ~output:
invoking: stderrtest0.sh test2 >&1 ~output:
on test2: this is an info
on test2: this is just text
invoking: stderrtest0.sh test3 2>&1 ~output:
on test3: this is an error
on test3: this is an info
on test3: this is just text
invoking: stderrtest0.sh test4 1>&2 ~output:
invoking: stderrtest0.sh test5 ~output:
on test5: this is an info
on test5: this is just text
$ cat err.out
on test1: this is an error
on test1: this is an info
on test1: this is just text
on test2: this is an error
on test4: this is an error
on test4: this is an info
on test4: this is just text
on test5: this is an error
And here I get confused.
Because in METATEST3 test1:
all output from stderrtest0.sh gets redirected to stderr
& so goes to the terminal but not to inf.out
And yet here in METATEST4 test2:
all output from stderrtest1.sh is being redirected to stdout
~BUT the stderr
emitted by stderrtest0.sh somehow escapes ?
So this seems to imply:
stderr
. stderr
output from some called program is not redirected into the caller's stdout
.
Is this the case?
Upvotes: 1
Views: 1141
Reputation: 1932
Ok! Thank you Ignacio !
I think I was getting hung up on the s/h notation & the meaning of redirection. Understanding this in terms of copying a file descriptor makes it clearer to me. Because I'm a bit thick, I'm going to labour the point & maybe you can correct me if I'm still not getting it...
Firstly:
echo "foo" >&2
This does not redirect any/all of echo's output to stderr
; it is a s/h equivalent to echo "foo" 1>&2
. It sets the stdout
FD of the echo process to be the same as it's FD for stderr
.
So then for:
{ echo "foo" 2>&1 >&2 ; } > /dev/null
Here, the stderr
FD for the echo process is set to the stdout
FD; then the stdout
FD is set to the stderr
FD ~accomplishing nothing since the new assignment is the same as the current value. And nothing gets to the terminal because the function's stdout
FD is set to /dev/null
And then for:
{ echo "foo" >&2 ; } > /dev/null 2>&1
Here the stdout
FD of the echo process is set to the stderr
FD. The function's stdout
FD is set to /dev/null and the function's stderr
FD is set to the value of the stdout
FD ~which is of course /dev/null. &So nothing gets to the terminal.
Or to put it another way:
$ { echo "phew"; echo "bah" >&2; }
phew
bah
$ { echo "phew"; echo "bah" >&2; } 2>/dev/null
phew
$ { echo "phew"; echo "bah" >&2; } 1>/dev/null
bah
$ { echo "phew"; echo "bah" >&2; } 1>/dev/null 2>&1
$
I am a numpty.
&BTW I like how you use { a function } instead of having to write out whole silly scripts. Much neater ;)
Upvotes: 0
Reputation: 799430
Normally the shell opens two separate file descriptors for stdout (1) and stderr (2). To redirect the output from a program sent to one to the other, it suffices to copy the FD from the other.
$ { echo "foo" >&2 ; } > /dev/null
foo
$ { echo "foo" 2>&1 >&2 ; } > /dev/null
$ { echo "foo" >&2 ; } > /dev/null 2>&1
$
Upvotes: 2