I spent 2+ hours working on a function to sync my private notes Repos. Actually getting the initial concept to work was quick, but then I wanted to make it work in parallel, easy. But then I wanted to capture the output and make it print out in serial. Not so easy.
Here is the my final solution:
notessync() {
set +m
declare -A outputs=()
local msg="===-<output>-===\n"
local dirs=(
$HOME/docs/corpus
$HOME/docs/notes/corpus
$HOME/docs/notes/captainslog
)
echo "===-<starting>-==="
for dir in ${dirs[@]}; do
outputs[$dir]=$(mktemp /tmp/notessync.XXX)
{ run-job $dir 2 >&$outputs[$dir] &>1 & } 2>/dev/null
done
wait
for dir in ${dirs[@]}; do
msg+="$(cat $outputs[$dir])\n"
done
echo $msg
set -m
}
What did I learn?
- I need to store output to a file
- This was done with a temp file (huray mktemp!)
outputs[$dir]=$(mktemp /tmp/notessync.XXX)
- This was done with a temp file (huray mktemp!)
- I learned about associative arrays in bash
- declare -A output=()
- i.e. output["foo"]="bar"
- How to capture the output with >&
- i.e. echo "foo" >&/tmp/some/file 2>&1
- the end bit pipes stderr to stdout which is piped by >& to the tmp file
- I need to set +m (disables job control?)
- with that disabled, it removes some output about currently running background tasks
- use
wait
to wait for background tasks to complete before ending function - then fold outputs back into msg and print out
Remaining Questions
I use a bash formatter. One weird thing I noticed is that the formatter would re-arrange the order of things
# from
run-job $dir >&$outputs[$dir] 2&>1
# to
run-job $dir 2 >&$outputs[$dir] &>1
Not sure what that's about but it doesn't seem to change the outcome. 🤷
Edit - 03-26-2022
Ok, sometimes I'm an idiot and sometimes my dyslexia sneaks one by.
The above re-arranging was do to a syntax error. It should be
run-job $dir >&$outputs[$dir] 2>&1
Also, >& some-file
is equivalent to > some-file 2>&1
. So the last redirect is not required.
On top of that, the preferred syntax is &>some-file
. The result:
run-job $dir &>$outputs[$dir]
I read it as apply run-job to $dir
, store stdout
to $outputs[$dir]
and redirect stderr to stdout
I've left the original script above with the mentioned errors, but below is the script with the issues resolved.
notessync() {
set +m
declare -A outputs=()
local msg="===-<output>-===\n"
local dirs=(
$HOME/docs/corpus
$HOME/docs/notes/corpus
$HOME/docs/notes/captainslog
)
echo "===-<starting>-==="
for dir in ${dirs[@]}; do
outputs[$dir]=$(mktemp /tmp/notessync.XXX)
{ run-job $dir &>$outputs[$dir] & } 2>/dev/null
done
wait
for dir in ${dirs[@]}; do
msg+="$(cat $outputs[$dir])\n"
done
echo $msg
set -m
}