Reputation: 19857
When The Script below is ran with no modifications, it outputs (correctly):
two one START
After uncommenting the exec
statements in the script below, file descriptor 3 points to standard output, and file descriptor 4 points to standard error:
exec 3>&1 4>&2
And all standard output & standard error gets logged to a file instead of printing to the console:
# Two variances seen in the script below
exec 1>"TEST-${mode:1}.log" 2>&1 # Log Name based on subcommand
exec 1>"TEST-bootstrap.log" 2>&1 # Initial "bootstrap" log file
When the exec statements are in place, the script should create three files (with contents):
TEST-bootstrap.log
(sucessfully created - empty file)
TEST-one.log
(sucessfully created)
one START
TEST-two.log
(is not created)
two one START
But instead it seems to stop after the first log file and never creates TEST-two.log
. What could be the issue considering it works with the exec
statements commented out?
#!/bin/bash
SELFD=$(cd -P $(dirname ${0}) >/dev/null 2>&1; pwd) # Current scripts directory
SELF=$(basename ${0}) # Current scripts name
SELFX=${SELFD}/${SELF} # The current script exec path
fnOne() { echo "one ${*}"; }
fnTwo() { echo "two ${*}"; }
subcommand() {
# exec 3>&1 4>&2
# exec 1>"TEST-${mode:1}.log" 2>&1
case "${mode}" in
-one) fnOne ${*}; ;;
-two) fnTwo ${*}; ;;
esac
}
bootstrap() {
# exec 3>&1 4>&2
# exec 1>"TEST-bootstrap.log" 2>&1
echo "START" \
| while read line; do ${SELFX} -one ${line}; done \
| while read line; do ${SELFX} -two ${line}; done
exit 0
}
mode=${1}; shift
case "${mode:0:1}" in
-) subcommand ${*} ;;
*) bootstrap ${mode} ${*} ;;
esac
exit 0
This script is strictly an isolated example of the problem I am facing in another more complex script of mine. I've tried to keep it as concise as possible but will expand upon it if needed.
In The Script above, I am using while loops instead of gnu-parallel for simplicity sake, and am using simple functions that echo "one" and "two" for ease of debugging & asking the question here.
In my actual script, the program would have the following functions: list-archives
, download
, extract
, process
that would fetch a list of archives from a URL, download them, extract their contents, and process the resulting files respectively. Considering these operations take a varying amount of time, I planned on running them in parallel. My script's bootstrap()
function would look something like this:
# program ./myscript
list-archives "${1}" \
| parallel myscript -download \
| parallel myscript -extract \
| parallel myscript -process
And would be called like this:
./myscript "http://www.example.com"
What I'm trying to accomplish is a way to start a program that can can call it's own functions in parallel and record everything it does. This would be useful to determine when the data was last fetched, debug any errors, etc.
I'd also like to have the program record logs when it's invoked with a subcommand, e.g.
# only list achives
>> ./myscript -list-archives "http://www.example.com"
# download this specific archive
>> ./myscript -download "http://www.example.com/archive.zip"
# ...
Upvotes: 0
Views: 224
Reputation: 19857
I've accepted @Barmar's answer as the answer and the comment left by @Wumpus Q. Wumbley nudged me in the right direction.
The solution was to use tee /dev/fd/3
on the case statement like so:
subcommand() {
exec 3>&1 4>&2
exec 1>"TEST-${mode:1}.log" 2>&1
case "${mode}" in
-one) fnOne ${*}; ;;
-two) fnTwo ${*}; ;;
esac | tee /dev/fd/3
}
Upvotes: 0
Reputation: 781058
I think this is the problem:
echo "START" \
| while read line; do ${SELFX} -one ${line}; done \
| while read line; do ${SELFX} -two ${line}; done
The first while
loop reads START
from the echo
statement. The second while
loop processes the output of the first while
loop. But since the script is redirecting stdout to a file, nothing is piped to the second loop, so it exits immediately.
I'm not sure how to "fix" this, since it's not clear what you're trying to accomplish. If you want to feed something to the next command in the pipeline, you can echo to FD 3, which you've carefully used to save the original stdout.
Upvotes: 2