From: Radoulov, Dimitre on
On 29/12/2009 21.12, mop2 wrote:
> On Tue, 29 Dec 2009 15:11:01 -0200, Radoulov, Dimitre
> <cichomitiko(a)gmail.com> wrote:
>
>> Hi all,
>> There was a question on a web forum on how to redirect stdin and stderr
>> in the following manner:
>>
>> - two separate files for both streams
>> - a third file containing both streams in their original order
>>
>> We haven't come up with a solution and I'm curious to hear your opinion.
>>
>> For instance, let's say we have the following shell script:
>>
>>
>> % cat s
>> #!/bin/sh
>>
>> for i in 1 2; do
>> for j in 1 2; do
>> printf '%s\n' "out $i"
>> done
>> for k in 1 2; do
>> printf '%s\n' "err $i" >&2
>> done
>> done
>>
>>
>> The script produces the following output on the terminal (which of
>> course includes both stdout and stderr streams):
>>
>> % ./s
>> out 1
>> out 1
>> err 1
>> err 1
>> out 2
>> out 2
>> err 2
>> err 2
>>
>> Note that we use out/err n only for clarity and easy debugging. We
>> assume that in the real world both the content and the order of the
>> stdout and stderr streams are unknown.
>>
>> Now the question is, is it possible to end up with the following files
>> after running the script:
>>
>> out.log
>>
>> out 1
>> out 1
>> out 2
>> out 2
>>
>> err.log
>>
>> err 1
>> err 1
>> err 2
>> err 2
>>
>> both.log
>>
>> out 1
>> out 1
>> err 1
>> err 1
>> out 2
>> out 2
>> err 2
>> err 2
>>
>>
>> Note the order of the entries in both.log.
>>
>> We also assume that it's not possible to modify the script/program that
>> generates the output in any way.

[...]

> Just an idea:
>
>
> $ cat s
> #!/bin/sh
>
> for i in 1 2; do
> for j in 1 2; do
> printf '%s\n' "out $i"
> done
> for k in 1 2; do
> printf '%s\n' "err $i" >&2
> done
> done
>
> $ cat test
>
> printf '' >b
> printf '' >o
> printf '' >e
>
> tail -n0 -f o >>b & To=$!
> tail -n0 -f e >>b & Te=$!
> ./s >o 2>e
> read -t2; kill $To $Te
>
> $ . ./test
> [1]- Terminated tail -n0 -f o >> b
> [2]+ Terminated tail -n0 -f e >> b
>
> $ cat o
> out 1
> out 1
> out 2
> out 2
>
> $ cat e
> err 1
> err 1
> err 2
> err 2
>
> $ cat b
> out 1
> out 1
> err 1
> err 1
> out 2
> out 2
> err 2
> err 2
[...]

Thanks, but I don't get the correct order in b:


% head -10 s test
==> s <==
#!/bin/sh

for i in 1 2; do
for j in 1 2; do
printf '%s\n' "out $i"
done
for k in 1 2; do
printf '%s\n' "err $i" >&2
done
done

==> test <==

printf '' >b
printf '' >o
printf '' >e

tail -n0 -f o >>b & To=$!
tail -n0 -f e >>b & Te=$!
../s >o 2>e
read -t2; kill $To $Te


Here is what I get:

zsh-4.3.10[t]% . ./test
[2] 1851
[3] 1852
zsh-4.3.10[t]%
[3] + terminated tail -n0 -f e >> b
zsh-4.3.10[t]%
[2] + terminated tail -n0 -f o >> b
zsh-4.3.10[t]% head -8 e o b
==> e <==
err 1
err 1
err 2
err 2

==> o <==
out 1
out 1
out 2
out 2

==> b <==
out 1
out 1
out 2
out 2
err 1
err 1
err 2
err 2


zsh-4.3.10[t]% ksh
ksh-JM 93t+ 2009-05-01$ . ./test
[1] 1857
[2] 1858
[2] + Terminated . ./test
[1] - Terminated . ./test
ksh-JM 93t+ 2009-05-01$ head -8 e o b
==> e <==
err 1
err 1
err 2
err 2

==> o <==
out 1
out 1
out 2
out 2

==> b <==
err 1
err 1
err 2
err 2
ksh-JM 93t+ 2009-05-01$

ksh-JM 93t+ 2009-05-01$ bash
bash-4.0.33(1)[t]$ . ./test
bash-4.0.33(1)[t]$ head -8 e o b
==> e <==
err 1
err 1
err 2
err 2

==> o <==
out 1
out 1
out 2
out 2

==> b <==
out 1
out 1
out 2
out 2
err 1
err 1
err 2
err 2
[1]- Terminated tail -n0 -f o >> b
[2]+ Terminated tail -n0 -f e >> b
bash-4.0.33(1)[t]$

What shell and OS are you using?



Regards
Dimitre
From: Icarus Sparry on
On Tue, 29 Dec 2009 18:11:01 +0100, Radoulov, Dimitre wrote:

> Hi all,
> There was a question on a web forum on how to redirect stdin and stderr
> in the following manner:
>
> - two separate files for both streams - a third file containing both
> streams in their original order
>
> We haven't come up with a solution and I'm curious to hear your opinion.

For typical programs, this is very hard. The reason is that almost all
programs use the "stdio" (standard I/O) library.

stdio has a default buffering strategy for stderr, that says it is
unbuffered. So sending anything to stderr makes it appear pretty much
straight away.

The strategy for stdout depends on if the library thinks it is wtiting to
a terminal or not. If it is not then the output only gets sent when an
internal buffer is full or the program is terminating. If it is writing
to a terminal then output gets sent when a line is completed.

So this means that a program

int main(int ac, char *av[]){ printf("Hello\n");
fprintf(stderr,"World\n"); return 0;}

will output the two words in a different order depending on if it is
being run with the output going to a pipe (or file) or if it is going to
a console.

If we assume that you want the "console" output order, then you need to
convince the stdio library that your program is talking to a console.
There are a couple of ways you can do this, I like an old program "pty",
but for most people "expect" is easier to find. It comes with an example
script "unbuffer" that does exactly what we want.

At this point you then just need to have a program that listens on
multiple inputs and writes to multiple outputs, and a way to tie them
together. Such a program is "multitee" (from the multitee package in
Debian).

One way to tie them together is with a named pipe.

# Create a pipe for the stderr of the program.
mkfifo sepipe
{ sleep 1 # To make sure that the shell sets up a reader for sepipe
unbuffer program 2>sepipes
rm sepipe
} | multitee 3<sepipe >stdout 2>stderr 4>both 0:1,4 3:2,4

Shells such as "rc" allow you to tie multiple steams together in a
pipeline in a reasonably easy way.

You might look at the thread
http://groups.google.com/group/comp.unix.shell/browse_thread/thread/
dbe5abeac7635d81/5ed189b9dfb75699


From: mop2 on
On Tue, 29 Dec 2009 18:55:28 -0200, Radoulov, Dimitre <cichomitiko(a)gmail.com> wrote:
~
> ksh-JM 93t+ 2009-05-01$ bash
> bash-4.0.33(1)[t]$ . ./test
> bash-4.0.33(1)[t]$ head -8 e o b
> ==> e <==
> err 1
> err 1
> err 2
> err 2
>
> ==> o <==
> out 1
> out 1
> out 2
> out 2
>
> ==> b <==
> out 1
> out 1
> out 2
> out 2
> err 1
> err 1
> err 2
> err 2
> [1]- Terminated tail -n0 -f o >> b
> [2]+ Terminated tail -n0 -f e >> b
> bash-4.0.33(1)[t]$
>
> What shell and OS are you using?

My system is a slackware:

$ uname -a
Linux k7 2.6.31.6 #1 PREEMPT Sat Nov 21 05:39:49 hrV 2009 i686 GNU/Linux

$ tail --version|head -n1
tail (GNU coreutils) 8.2

$ $0 --version|head -n1
GNU bash, version 4.0.35(1)-release (i686-pc-linux-gnu)

$ ls -l /lib/libc.so*
lrwxrwxrwx 1 root root 11 2009-11-20 19:47 /lib/libc.so.6 -> libc-2.9.so*

The more important here, I think, is the program tail.
If the "-s" is available you can try it to see if behavior changes for small values:
-s, --sleep-interval=N
with -f, sleep for approximately N seconds (default 1.0) between iterations

I never tried this option, but in the past I had problems with buffering, as said by others.
I really like use exclusively shell when this is not inconvenient , and this option always
solves to me buffering and delay problems.

So, a new test, using 2 shell loops in background an your script "s":

$ cat test1

printf '' >b
printf '' >o
mkfifo fo
printf '' >e
mkfifo fe

while read;do printf "$REPLY\n";printf "$REPLY\n">>b;done >o <fo &
while read;do printf "$REPLY\n";printf "$REPLY\n">>b;done >e <fe &

../s >fo 2>fe

rm fo fe

head o e b


$ . ./test1
[1]- Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >> b;
done > o < fo
[2]+ Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >> b;
done > e < fe
==> o <==
out 1
out 1
out 2
out 2

==> e <==
err 1
err 1
err 2
err 2

==> b <==
out 1
out 1
err 1
err 1
out 2
out 2
err 2
err 2
From: Han Pingtian on
On 2009-12-29, Icarus Sparry <usenet(a)icarus.freeuk.com> wrote:
> On Tue, 29 Dec 2009 18:11:01 +0100, Radoulov, Dimitre wrote:
>
>> Hi all,
>> There was a question on a web forum on how to redirect stdin and stderr
>> in the following manner:
>>
>> - two separate files for both streams - a third file containing both
>> streams in their original order
>>
>> We haven't come up with a solution and I'm curious to hear your opinion.
>
> For typical programs, this is very hard. The reason is that almost all
> programs use the "stdio" (standard I/O) library.
>
> stdio has a default buffering strategy for stderr, that says it is
> unbuffered. So sending anything to stderr makes it appear pretty much
> straight away.
>
> The strategy for stdout depends on if the library thinks it is wtiting to
> a terminal or not. If it is not then the output only gets sent when an
> internal buffer is full or the program is terminating. If it is writing
> to a terminal then output gets sent when a line is completed.
>
> So this means that a program
>
> int main(int ac, char *av[]){ printf("Hello\n");
> fprintf(stderr,"World\n"); return 0;}
>
> will output the two words in a different order depending on if it is
> being run with the output going to a pipe (or file) or if it is going to
> a console.
>
> If we assume that you want the "console" output order, then you need to
> convince the stdio library that your program is talking to a console.
> There are a couple of ways you can do this, I like an old program "pty",
> but for most people "expect" is easier to find. It comes with an example
> script "unbuffer" that does exactly what we want.
>
> At this point you then just need to have a program that listens on
> multiple inputs and writes to multiple outputs, and a way to tie them
> together. Such a program is "multitee" (from the multitee package in
> Debian).
>
> One way to tie them together is with a named pipe.
>
> # Create a pipe for the stderr of the program.
> mkfifo sepipe
> { sleep 1 # To make sure that the shell sets up a reader for sepipe
> unbuffer program 2>sepipes
> rm sepipe
> } | multitee 3<sepipe >stdout 2>stderr 4>both 0:1,4 3:2,4
>
> Shells such as "rc" allow you to tie multiple steams together in a
> pipeline in a reasonably easy way.
>
> You might look at the thread
> http://groups.google.com/group/comp.unix.shell/browse_thread/thread/
> dbe5abeac7635d81/5ed189b9dfb75699
>
>
It seems that the unbuffer would combine the stdout and stderr:

sh-4.0$ cat t.
t.sh t.txt
sh-4.0$ cat t.sh
#!/bin/bash


for i in 1 2; do
for j in 1 2; do
printf '%s\n' "out $i"
done
for k in 1 2; do
printf '%s\n' "err $i" >&2
done
done
sh-4.0$ ./t.sh 2>&1 >/dev/null
err 1
err 1
err 2
err 2
sh-4.0$ unbuffer !!
unbuffer ./t.sh 2>&1 >/dev/null
sh-4.0$



--
Han Pingtian
From: Radoulov, Dimitre on
On 30/12/2009 2.44, mop2 wrote:
> On Tue, 29 Dec 2009 18:55:28 -0200, Radoulov, Dimitre
> <cichomitiko(a)gmail.com> wrote:
> ~
>> ksh-JM 93t+ 2009-05-01$ bash
>> bash-4.0.33(1)[t]$ . ./test
>> bash-4.0.33(1)[t]$ head -8 e o b
>> ==> e <==
>> err 1
>> err 1
>> err 2
>> err 2
>>
>> ==> o <==
>> out 1
>> out 1
>> out 2
>> out 2
>>
>> ==> b <==
>> out 1
>> out 1
>> out 2
>> out 2
>> err 1
>> err 1
>> err 2
>> err 2
>> [1]- Terminated tail -n0 -f o >> b
>> [2]+ Terminated tail -n0 -f e >> b
>> bash-4.0.33(1)[t]$
>>
>> What shell and OS are you using?
>
> My system is a slackware:
>
> $ uname -a
> Linux k7 2.6.31.6 #1 PREEMPT Sat Nov 21 05:39:49 hrV 2009 i686 GNU/Linux

I tested the script on Ubuntu 9.10 on Sun VirtualBox (Windows XP guest)
and on a real (physical) RHEL 5.1.


> $ tail --version|head -n1
> tail (GNU coreutils) 8.2
>
> $ $0 --version|head -n1
> GNU bash, version 4.0.35(1)-release (i686-pc-linux-gnu)
>
> $ ls -l /lib/libc.so*
> lrwxrwxrwx 1 root root 11 2009-11-20 19:47 /lib/libc.so.6 -> libc-2.9.so*

Just for completeness:

Ubuntu VM:

$ tail --version|head -n1
tail (GNU coreutils) 7.4

$ $0 --version|head -n1
GNU bash, version 4.0.33(1)-release (i486-pc-linux-gnu)

$ ls -l /lib/libc.so*
lrwxrwxrwx 1 root root 14 2009-12-26 11:20 /lib/libc.so.6 -> libc-2.10.1.so


RHEL:

$ tail --version|head -n1
tail (GNU coreutils) 5.97

$ $0 --version|head -n1
GNU bash, version 3.1.17(1)-release (i686-redhat-linux-gnu)

$ ls -l /lib/libc.so*
lrwxrwxrwx 1 root root 11 Jan 7 2008 /lib/libc.so.6 -> libc-2.5.so



> The more important here, I think, is the program tail.
> If the "-s" is available you can try it to see if behavior changes for
> small values:
> -s, --sleep-interval=N
> with -f, sleep for approximately N seconds (default 1.0) between iterations
>
> I never tried this option, but in the past I had problems with
> buffering, as said by others.
> I really like use exclusively shell when this is not inconvenient , and
> this option always
> solves to me buffering and delay problems.

I tried with values from 0.1 to 0.5, but I get the same result.
Later I will try the test again on a different system.


> So, a new test, using 2 shell loops in background an your script "s":
>
> $ cat test1
>
> printf '' >b
> printf '' >o
> mkfifo fo
> printf '' >e
> mkfifo fe
>
> while read;do printf "$REPLY\n";printf "$REPLY\n">>b;done >o <fo &
> while read;do printf "$REPLY\n";printf "$REPLY\n">>b;done >e <fe &
>
> ./s >fo 2>fe
>
> rm fo fe
>
> head o e b
>
>
> $ . ./test1
> [1]- Done while read; do
> printf "$REPLY\n"; printf "$REPLY\n" >> b;
> done > o < fo
> [2]+ Done while read; do
> printf "$REPLY\n"; printf "$REPLY\n" >> b;
> done > e < fe
> ==> o <==
> out 1
> out 1
> out 2
> out 2
>
> ==> e <==
> err 1
> err 1
> err 2
> err 2
>
> ==> b <==
> out 1
> out 1
> err 1
> err 1
> out 2
> out 2
> err 2
> err 2


The order in b is not constant between executions. This is on RHEL 5.1:


$ . ./test1
[1]- Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >>b;
done >o <fo
[2]+ Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >>b;
done >e <fe
==> o <==
out 1
out 1
out 2
out 2

==> e <==
err 1
err 1
err 2
err 2

==> b <==
out 1
out 1
out 2
out 2
err 1
err 1
err 2
err 2
$ . ./test1
[1]- Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >>b;
done >o <fo
[2]+ Done while read; do
printf "$REPLY\n"; printf "$REPLY\n" >>b;
done >e <fe
==> o <==
out 1
out 1
out 2
out 2

==> e <==
err 1
err 1
err 2
err 2

==> b <==
out 1
err 1
out 1
err 1
err 2
err 2
out 2
out 2


Thanks again for the ideas anyway.

Regards
Dimitre
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5
Prev: list out thousand of files
Next: unzip | touch | re-zip