I have a backup script which I'm asking now to (offer to) call a git pull script before actually running the syncs (when run interactively). The syncs are done thusly:
rsync aiSP --delete
The backup contains this redirection:
exec 2> "${const_logfile}"
The git pull script has these two lines:
printf '%s\n' "Shall we pull all the gits? "
read -rep "(You will have to enter your git password for each one.) (y or N) " -n1
When rsync runs, all of its progress output shows in the terminal (and doesn't get stuffed into the log file). Any error in rsync is added to the logfile.
When the git pull script is called by the backup script the printf line displays in the terminal, but the read text gets stuffed into the log file (and not displayed). This doesn't happen when that script is called directly.
So I pulled that line of redirect code into a test script that contained only that and the git pull script call, so I'm certain that's the culprit. Finally, I added the redirect code directly into the git pull script and called that directly, and the read text again failed to show in the terminal and was sent to the log file.
I would expect the printf text and the read text to go the same route?
I'm sure there's a simple explanation. Or complex. Enlighten me, please.
EDIT:
Raj was helpful; thanks, Raj!
However, there is more I'd like to understand; so, if anyone knows of an article that talks about what qualifies as these extraordinary messages (as Raj has called them) and why it's useful to send them to stderr, I'd love to read more about this.
Also, someone following along may like to know about the -p aspect of read (from here):
-p prompt Display prompt on standard error, without a trailing newline, before attempting to read any input. The prompt is displayed only if input is coming from a terminal.
The key to answer is something that you already written in your question: "When rsync runs, all of its progress output shows in the terminal (and doesn't get stuffed into the log file). Any error in rsync is added to the logfile."
That's because the
exec
command redirects the standard error (but NOT standard output) of the shell that runs the script to the logfile. Regularrsync
output is written to stdout (ie. to the terminal), but error messages are written to stderr (ie. to the log file).It's a commonly used convention in Linux tools that "normal" output is written to stdout, and any "extraordinary" messages that should be shown to the user even when the output is redirected are written to stderr.
Thus, the
read
prompt is also written to stderr, as it is intended to be shown to the user. On the other hand,printf
is intended to display "regular" output, so it writes to stdout (if you want to write to stderr, you can always useprintf ... >&2
).Similarly, if you want
read
prompt to be written to stdout, you can useread ... 2>&1
. But what if stdout is redirected?The correct solution is to remember the "initial" stderr, before it gets redirected by
exec
(which we hope is a terminal) in another file descriptor and use that descriptor to display the prompt on the "original" stderr.Before the
exec
line that you quoted you should put another one:This duplicates the file descriptor 2 (stderr) to file descriptor 3 before the next
exec
command redirects it. Then inread
command use the following redirection:This should redirect
read
prompt to file descriptor 3, ie. the original stderr before redirection.