Standard output and standard error
TLDR (Too Long, Didn't Read)
I realize that this post is very long-winded, so here are the actual useful bits:
If a shell command is printing to STDERR, but you want to pipe it to a program, add
2>&1
just before the|
.Discard STDERR by adding
2> /dev/null
to the end of the command.
I'm currently learning a song from Nintendo's Game & Watch Gallery (the "Fire" game) on Chapman Stick. To do so, I'm using the *.gbs file of the game.
I found out about the gbsplay project from the Wikipedia entry for *.gbs files. I decided to download the tarball and to try compiling it on my CentOS machine.
After a successful make in the source directory, I ran the newly-made gbsplay binary with the *.gbs for Game & Watch Gallery as the argument. Unfortunately, the TUI controls were quickly scrolled off screen due to errors that were spewing from one of the channels in the song I was playing:
$ pwd /home/tstanton/software/source/gbsplay/gbsplay-0.0.91 $ ./gbsplay ~/tmp/Game\ \&\ Watch\ Gallery\ \(1997\)\(Nintendo\).gbs \ 5 Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! GBSVersion: 1 Title: "Game & Watch Gallery" Author: "<?>" Copyright: "1997 Nintendo" Load address: 0x10ab Init address: 0x10ab Play address: 0x11fe Stack pointer: 0xdfef File size: 0x00006fc5 ROM size: 0x00008000 (2 banks) Subsongs: 15 commands: [p]revious subsong [n]ext subsong [q]uit player [ ] pause/resume [1-4] mute channel ^MSong 5/ 15 (Untitled) 00:00/02:00 --- A-5 %%% --- --- [ | ] Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! ^MSong 5/ 15 (Untitled) 00:00/02:00 --- A-5 %%# --- --- [ #|# ] Bank 7 out of range (0-1)! Bank 7 out of range (0-1)! ^MSong 5/ 15 (Untitled) 00:00/02:00 --- A-5 %%= --- --- [ #|# ] ...
I know how to discard STDERR, but I wanted to see if I could ask the
program to simply not print the errors in the first place. I could see
that there's a -q
option that might do that:
$ ./gbsplay --help ./gbsplay: invalid option -- - Usage: ./gbsplay [option(s)] <gbs-file> [start_at_subsong [stop_at_subsong] ] Available options are: ... -q reduce verbosity ...
I now realize that the program does not take long options, only short.
Passing a --help
option was giving me the usage that I wanted, but it
was doing so on STDERR (notice the brief error message that prints first).
Not knowing this, I proceeded to try to pipe the usage output to less
so that I could have the text handy in a suspended job. It didn't work.
I had a hunch that the usage information was being printed to STDERR,
not to STDOUT. I couldn't quite remember what the shell syntax is to
merge the streams, as you can see from my history:
$ history 300 ... 1400 ./gbsplay --help | less 1401 ./gbsplay --help &2>1 | less 1402 ./gbsplay --help &1>2 | less 1403 ./gbsplay --help &2>1 | less 1404 ./gbsplay --help 2&>1 | less 1405 ./gbsplay --help 2>&1 | less ...
It took me 5 tries to get the syntax right (command #1405 pipes it to
less
successfully). Pretty embarrassing ("Hmm...maybe the ampersand
goes...here!"). The last 2 times I literally just used
CTRL-t
to move the ampersand along it's merry way.. Additionally, a pleasant
side effect of this is that I had accidentally littered my working
directory with two files named 1
and 2
in my failed attempts.
So back to the original thing I was trying to solve. I added the -q
option to my command to play the *.gbs, hoping that those errors would
be suppressed. They weren't.
Time for more drastic measures. Having faith that those "out of range"
errors were indeed being printed to STDERR, I knew that I could simply
discard STDERR by piping it to the magical /dev/null
file:
$ ./gbsplay ~/tmp/Game\ \&\ Watch\ Gallery\ \(1997\)\(Nintendo\).gbs \ 5 2> /dev/null GBSVersion: 1 Title: "Game & Watch Gallery" Author: "<?>" Copyright: "1997 Nintendo" Load address: 0x10ab Init address: 0x10ab Play address: 0x11fe Stack pointer: 0xdfef File size: 0x00006fc5 ROM size: 0x00008000 (2 banks) Subsongs: 15 commands: [p]revious subsong [n]ext subsong [q]uit player [ ] pause/resume [1-4] mute channel Song 5/ 15 (Untitled) 00:03/02:00 --- A-5 %%- A#3 %%% --- [ #%%|%%# ]
I ended up not using -q
, since it turned out that that option was
removing the awesome ASCII art that the program displays about the note
being played.
That worked! Problem solved. My TUI is intact. :) I am still curious as to why gbsplay is having trouble with playing some of the melody of this song though. :(
gbsplay source code investigation
I didn't actually realize that --help
was incorrect until I looked at
gbsplay.c
, out of curiosity. I wanted to see where usage information
was being printed to STDERR.
I found that if I had called the program with the correct -h
option,
the usage information would have been printed to STDOUT, as I expected.
gbsplay.c (licensed under GNU GPL)
static regparm void parseopts(int *argc, char ***argv) { long res; myname = *argv[0]; while ((res = getopt(*argc, *argv, "1234E:f:g:hlo:qr:R:t:T:vVzZ")) != -1) { switch (res) { default: usage(1); break; ... case 'h': usage(0); break; ...
...and we can see in the function definition that the usage text can be
printed on either STDERR or STDOUT, depending on the exitcode
argument
given to usage().
static regparm void usage(long exitcode) { FILE *out = exitcode ? stderr : stdout; fprintf(out, _("Usage: %s [option(s)] <gbs-file> [start_at_subsong [stop_at_subsong] ]\n" "\n" "Available options are:\n" " -E endian, b == big, l == little, n == native (%s)\n" " -f set fadeout (%ld seconds)\n" " -g set subsong gap (%ld seconds)\n" " -h display this help and exit\n" ...