3

I have a shell script that's happily doing all my backup process and writing progress to stderr and stdout, with one exception — the last line doesn't get written to my terminal (if I call the script manually) until I hit Enter.

Now this isn't a big deal, but it's really irritating me that it's not doing it, so I'd like to understand why it's behaving that way.

What gets written to the output (after I hit Enter) reads:

[blackero@XXXXXXXXXXXXXXXX ~]$ fullbackup.sh
Tue Feb 28 17:57:41 GMT 2012
Tue Feb 28 17:57:41 GMT 2012
Starting SITE_NAME backup
maintenance_mode was set to 1.                                                    [success]
/home/blackero/bin/fullbackup.sh: line 36: hash: lzma: not found
maintenance_mode was set to 0.                                                    [success]

Backup for SITE_NAME created


[blackero@XXXXXXXXXXXXXXXX ~]$

The relevant parts of the script read as follows (with indicating I've wordwrapped the code for legibility):

#!/bin/bash
date
date 2>&1

# [A bunch of content has been deleted from here]

# Compress with lzma if available, otherwise use gzip
if hash lzma; then
  lzma -c ${backup_dest}/${dbname} >${backup_dest}/${dbname}.lzma
  ↪  && rm ${backup_dest}/${dbname}
else
  gzip -c ${backup_dest}/${dbname} >${backup_dest}/${dbname}.gz
  ↪  && rm ${backup_dest}/${dbname}
fi

# Disable maintenance mode.
drush -r ${drupal_root} vset --always-set maintenance_mode 0

# Remove old backups >30 days
find ${backup_dest} -mtime +30 -exec rm {} \; >> /dev/null 2>&1

echo "Backup for ${sitename} created"
echo
echo 2>&1

So, you can see the if hash lzma; then line that's causing the hash: lzma: not found line to stderr (it'd be nice if I could suppress this message, so it doesn't write that warning, but I can live with it). You can see that drush is writing the maintenance_mode was set to 0. line to stdout. But the line Backup for SITE_NAME created only appears once I hit Enter.

Is that because the previous command is redirecting stdout to /dev/null? Do I need to undo that redirect somehow? (I thought stream redirection only affected the single command atomically.)

(Caveats: Fwiw, when this script is run from cron, with stdout and stderr redirected to append to logfiles, everything gets written to the logfiles quite happily, with no terminal input required. The script is originally from Drupal's Administration Guide: fullsitebackup_drush; I've edited it a fair amount, but the lzma or gzip code is from there.)

5
  • What happens if you hit enter while the script is running?
    – Mat
    Feb 28, 2012 at 19:28
  • 1
    Change hash lzma to type lzma >/dev/null 2>/dev/null. If you remove the calls to drush, find or both, do you observe the same behavior? Feb 28, 2012 at 19:29
  • 1
    can u try to first add -f to the rm command in find (add -- also for security reason) and also add echo before find and drush to see where indeed it gets stuck? (one possible reason I can think of is u don't have write access to the single file u need to remove in find and so rm prompt for a user input which is hidden by ur redirection, btw no need to use >> /dev/null, > /dev/null will be fine, and further more, u can use &>/dev/null to replace >>/dev/null 2>&1)
    – yuyichao
    Feb 28, 2012 at 20:44
  • 1
    Unrelated to your question, but don't use "find ... -exec rm {} \;" if you can help it, it's a security issue. See the info docs for GNU find for an explanation. Feb 28, 2012 at 23:22
  • Having made both the find ... -delete change plus amending the if statement to read if type lzma &>/dev/null; then means that I am no longer getting the hash: lzma: not found message and that I no longer need to press Enter to get a prompt again afterwards. Thank you all! Feb 29, 2012 at 11:05

1 Answer 1

7

If your backup files are read-only, it's possible that the rm command run by find is prompting you to confirm whether or not it should delete a backup file. You can't see the prompt because you redirected it to /dev/null. The Enter you press is read by rm (and makes rm take the default answer, which is not to delete the file).

If this hypothesis is correct you can fix the problem in one of two ways. One is portable but insecure, the other is nonportable but more secure. Even though in this case the security difference is likely small (since attackers probably can't create symlinks in ${backup_dest}) I'll suggest both options:

secure:

find "${backup_dest}" -depth -mtime +30 -delete

portable:

find "${backup_dest}" -depth -mtime +30 \
  '(' '(' -type d -exec rmdir {} \; -true ')' -o -exec rm -f {} \; ')'

Last, there is no need to use ">> /dev/null" instead of "> /dev/null" since there's no difference between appending to a character device and writing to it, since character devices are not seekable.

1
  • Having made both this change (the secure way, as it doesn't need to be portable in this context) plus changing the if statement to read if type lzma &>/dev/null; then means that I am no longer getting the hash: lzma: not found message and that I no longer need to press Enter to get a prompt again afterwards. Thank you all! Feb 29, 2012 at 11:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .