Just a quick something to keep this site alive.
These two issues were nothing that got me fuming angry, but my boss was not amused when I was blindsided by these two issues in a production environment.
There is this nice UNIX tool named `tee` that writes its standard input to a file without actually consuming it. It is very useful in scripts where you want to print the proceedings to screen while at the same time logging them somewhere:
someCommand | tee -a logfile.txt # '-a' to append instead of overwriting
It gets ugly, though, when you add this kind of logging to a script retroactively, and fail to realize that the next step in the original script was to check if someCommand
was successful:
someCommand | tee -a logfile.txt if [ $? != 0 ]; then …
Congratulations: Because $?
returns the exit code of the last command executed, you just checked whether tee
succeeded (e.g., in logging someCommand
’s failure).
Luckily, bash offers a way out: The array PIPESTATUS
carries the individual exit codes of all commands in the pipe chain.
someCommand | tee -a logfile.txt if [ {$PIPESTATUS[0]} != 0 ]; then …
Consider:
#include <string> #include <cassert> using namespace std; void foo( std::string & parmN, ... ) { va_list ap; va_start( ap, parmN ); int i = va_arg( ap, int ); va_end( ap ); } int main() { std::string broken; foo( broken, 42 ); return 0; }
The variable i
will most likely not be set to 42.
The reason is that most implementations of va_arg
will take the address of parmN
using the address operator &
. For a C++ reference, like in this case, this will yield the address of the referenced variable. You will not get the address of parmN
, but the address of broken
, and whatever you pull out of va_arg
will not be what you expected.
These two bastiches caused me some hurt. Be warned.