On Mon, 18 Jan 2016 10:09:14 +0900 Joel Rees <joel.r...@gmail.com> wrote: > Trying to put some scripts together so I can set an update going one > night, check it in the morning, reboot, and finish the update while > I'm at work. > > So I want to do something like > > cd /usr/src && cvs -d$CVSROOT up -Pd | tee /var/log/build/cvssrc.log > cd /usr/xenocara && cvs -d$CVSROOT up -Pd | tee > /var/log/build/cvsxenocara.log > cd /usr/ports && cvs -d$CVSROOT up -Pd | tee /var/log/build/cvsports.log > cd /usr/src/sys/arch/`machine`/conf && config GENERIC.MP && \ > cd ../compile/GENERIC.MP && make clean && make && \ > make install | tee /var/log/build/buildsys.log 2>&1 > ...
i've been down this road, and there are a few problems with this. you don't check to see if the commands fail before you move on, so if cvs fails, it will continue with the next cvs or make, etc. when you pipe something, the error code will come from the last command in the pipe, which would be tee, so you can't test the error code directly. one way around this is what i do for commands that fail is i have some functions: make_failed() { [ $# -ge 1 ] || return 1 [ ${#1} -gt 0 ] || return 1 tail -1 "$1" | grep -q "\*\*\* Error " } cvs_failed() [ $# -ge 1 ] || return 1 [ ${#1} -gt 0 ] || return 1 tail -1 "$1" | grep -q "\[update aborted\]" } which will return true if the last line has an error (since a function returns the error code of the last program executed, and grep here fails or not depending on the presence of the error string.) i use it like this: make 2>&1 | tee $_logfile_build make_failed $_logfile_build && errx "make failed" or using your example: cd /usr/src && cvs -d$CVSROOT up -Pd 2>&1 | tee /var/log/build/cvssrc.log cvs_failed /var/log/build/cvsrsc.log && errx "cvs src failed" where errx is another function of mine: err() { echo "$0: ERR $*" >&2; } errx() { echo "$0: ERR $*" >&2; exit 1; } i have an include file with basic functions i use in many of my scripts like this, at the head of the script i have a line: . ~/.func a few more notes on the shell. you can do something like if ! make;then err "make failed" fi 2>&1 | tee logfile which will send all output from the 'if' statement to tee, much like the { cmd1; cmd2; } examples you were given earlier. one issue with this is that when you pipe it like this, it spawns a subshell, so nothing in the if statement goes outside. an 'exit' will only exit the 'if' statement (or 'while', 'for', etc) and not the program, so my 'errx' function above does not exit the whole script, only the subshell. the same holds true for '(cmd)' statements, which start a subshell, which is why you can do (cd /usr/src && ls) and it returns to its original directory afterwards, since the 'cd' only changes the subshell's working directory. you also can't set varibles in '(cmd)' or in anything in a pipeline (like that 'if'), and have them carry over to the rest of the script. you CAN however set variables in '{ cmd; }' statements. a good place to learn some interesting shell techiniques in in things like the /etc/rc* scripts and the install scripts in /usr/src/distrib/miniroot/. sometimes you learn the hard way though through experimentation and failure (even if you RTFM sometimes the meaning only becomes clear after you experience it.)