6

In my Bash script I have this line:

 mv -f $HOME_DIR/$dir $HOME_SAVE/BACKUP

The problem is when $dir is empty (no $dir directory) the mv command moved the directory $HOME_DIR. We want only to move the $dir directories.

How can I avoid this case?

I can use this:

ls $HOME_DIR | wc -l   
# and verify if the number is -gt 1

But this is not an elegant solution.

1

4 Answers 4

8

You should use :

mv -f -- "$HOME_DIR/${dir:?}" "$HOME_SAVE/BACKUP/"

The expansion will fail, the script quit, and an error message will be emitted to stderr if $dir's value is empty.

You can even specify the message printed like:

mv -f -- "$HOME_DIR/${dir:?\$dir is UNSET or NULL!}" "$HOME_SAVE/BACKUP/"

Alternatively - and less ultimately - you can specify default and/or substitute values for $dir which will only be applied if it is :-unset or null or :+set and not null using a similar form. All of these examples - above and below - are representative of a few of several standard POSIX-specified parameter expansion forms.

In the below example when $dir is set and not null the first portion evaluates to your source directory, else to nothing at all and the second portion evaluates to its value if any, but if none it evals instead to your target dir. mv is specified to fail when its first and second args name the same pathname and so nothing is moved at all.

mv -f -- "${dir:+$HOME_DIR/}${dir:-$HOME_SAVE/BACKUP/}" "$HOME_SAVE/BACKUP/" 2>/dev/null

That is probably overkill though as I suppose:

mv -f -- "$HOME_DIR/$dir" ${dir:+"$HOME_SAVE/BACKUP/"} 2>/dev/null

...should do just as well - mv can't move anything nowhere. Note that in a POSIX-conforming shell the quotes even inside the {} curlies serve to protect the expansion because in that case it is not $dir's value expanded but instead ${dir:+word} word's value expanded. Putting them within the braces serves to eval the expansion to nothing at all - not even a null string - when ${dir} is unset or null. That probably doesn't matter really - I'm fairly certain a null filename is invalid pretty much everywhere - but it's how I usually do it. This is not safe to do with ${dir:-"word"} however - in that case you would get either ${dir}'s unquoted expansion or word's quoted expansion.

You might also optionally invoke the -interactive option to mv only if $dir is null like:

mv "-i${dir:+f}" -- "$HOME_DIR/$dir" "$HOME_SAVE/BACKUP/" </dev/tty

...and so you would be sure at least not to accidentally overwrite any files in .../BACKUP without someone first pressing a y (or whatever) at the terminal.

12
  • 1
    Moving /dev/null will either fail (from an account without write permission to /dev) or damage the system. Both ways it sounds like a rather bad idea. Commented Dec 4, 2014 at 15:57
  • @EmilJeřábek - it will fail almost certainly. not sure how it would damage the system though - its just a dev node. could you expand on this please?
    – mikeserv
    Commented Dec 4, 2014 at 16:00
  • 1
    If you happen to run this as root, it will move the device file to $HOME_SAVE/BACKUP, making it disappear for all the applications that expect /dev/null to exist. Actually, I’m not sure what happens if /dev is managed by devfs or similar (I’m on an old system at the moment), but it sounds dangerous enough even to try it for no good reason. Commented Dec 4, 2014 at 16:09
  • @EmilJeřábek yes - so apps expecting /dev/null get ebadf rather than eof. not terrible. i only used it in the definition because it is one of a very few paths guaranteed by posix to exist - and so it cannot be some sensitive file or another that is not spec'd - and, among those, i considered it the least likely to cause trouble - though i will admit it might be an issue causing the need to reboot - or else to mknod it again, i guess - in some scenarios - though i think that is the worst it might get.
    – mikeserv
    Commented Dec 4, 2014 at 16:14
  • 1
    So you consider it adequate that a script may potentially make unrelated applications fail, and break the very POSIX guarantee that you quote? Let alone the fact that it is not even reliable, as $HOME_DIR may easily be more than two directories deep, in which case it may access a completely different file? Commented Dec 4, 2014 at 16:30
5

POSIXly:

[ -n "$dir" ] && mv -f -- "$HOME_DIR/$dir" "$HOME_SAVE/BACKUP"

or:

[ -d "${dir:+$HOME_DIR/$dir}" ] && ...
5
  • 1
    That doesn't solve the problem when $dir is empty. Commented Dec 4, 2014 at 12:15
  • 1
    Yeah, I switch to -n for more simple version.
    – cuonglm
    Commented Dec 4, 2014 at 12:23
  • but shuld be like this [ -n $HOME_DIR/$dir ] ..... ( and if $dir is null the it will true because $HOME_DIR isnt null ) Commented Dec 4, 2014 at 12:39
  • @maihabunash - i dont think so - the point is to verify a null or not null - the test should always fail when $dir is null.
    – mikeserv
    Commented Dec 4, 2014 at 12:53
  • same thing here, too, huh? somebody ambled through and upvoted correction comments that no longer apply. silly.
    – mikeserv
    Commented Dec 5, 2014 at 13:49
3

Test if the dir variable is empty ([[ -z $dir ]]):

[[ -z $dir ]] || mv -t "$HOME_SAVE/BACKUP" "$HOME_DIR/$dir"
2
  • need to set afull PATH as [[ -z $HOME_DIR/$dir ]] ( but if $dir is empty then it will be anyway not null because $HOME_DIR ) Commented Dec 4, 2014 at 12:42
  • @maihabunash That test would always fail because of /.
    – muru
    Commented Dec 4, 2014 at 12:47
3

You can use this:

mv -f -- "$HOME_DIR/./$dir" "$HOME_SAVE/BACKUP/"

If $dir is empty, this will give a "Device or resource busy" error.

However, this is a bit of a hack.

1
  • thats pretty clever. i like hacks.
    – mikeserv
    Commented Dec 4, 2014 at 12:31

You must log in to answer this question.

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