2

I'm using Ubuntu 15.10. I have noticed a weird problem.

When running a rsync command embedded in a shell script (as root) with --exclude={} options, rsync works and excludes paths as it's supposed to when I launch it as root via

./rBackup.sh

However, when I run it as root via

sh rBackup.sh

the exclude directory options do not carry over, and it infinite loops when backing up the drive it's backing up to in /media/. Why?

Edit - this is not a sh / bash difference - or shouldn't be. I have #!/bin/bash as the first line, so when running via "sh *.sh" it should use the bash interpreter - at least, in theory.

Script is as follows:

#!/bin/bash

StandbyMount="/media/astump/sdb2"
mount /dev/sdb2 $StandbyMount
rsync -aAXv --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/media/*,/lost+found,/etc/fstab,/boot/grub/grub.cfg} --delete-before / $StandbyMount
umount $StandbyMount
rm -fr $StandbyMount
user3260912
  • 131
  • 6
  • Because sh calls dash, debian amquist shell, and those curly braces are probably interpreted as inputs to dash, not a standalone command – Sergiy Kolodyazhnyy Feb 09 '16 at 14:27
  • Edited the question to clarify. I do have #!/bin/bash as the first line, so in theory at least, it should use bash even when calling it via "sh *.sh" – user3260912 Feb 09 '16 at 16:25
  • 1
    @user3260912 doesn't matter if you have the #!/bin/bash there. If you call script with sh script.sh it will run with sh. – Sergiy Kolodyazhnyy Feb 09 '16 at 16:28

2 Answers2

5

When you run

sh rBackup.sh

the script rBackup.sh is being run by sh (dash) which does not support brace expansion.

On the other hand, when you do

./rBackup.sh

then the #!/bin/bash first line specifies the exact interpreter to use. It happens that bash supports brace expansion.

If you don't include a valid executable in the shebang line, you are simply running the script under the current shell you are in (or sh depending on the shell implementations).

Example: From bash:

$ cat scr.sh 
echo {bar,spam}

$ sh scr.sh 
{bar,spam}

$ ./scr.sh 
bar spam

My login shell is zsh. Here are some points to note:

  • While at zsh, ./scr.sh is being sent to sh(dash)

  • While at bash, ./scr.sh is being sent to bash

  • While at ksh, ./scr.sh is being sent to ksh

  • While at dash, ./scr.sh is being sent to sh (dash)


To be on the safer side always try to mention the desired interpreter using shebang (first line of the script) e.g.:

#!/usr/bin/env bash
Monty Harder
  • 297
  • 1
  • 6
heemayl
  • 91,753
  • If the shebang isn't valid, the /bin/sh will be used. See man 3 exec (Special semantics for execlp() and execvp()): If the header of a file isn't recognized (the attempted execve(2) failed with the error ENOEXEC), these functions will execute the shell (/bin/sh) with the path of the file as its first argument. – muru Feb 09 '16 at 19:10
  • @muru I don't see its happening apart from zsh (and dash`)..what gives? – heemayl Feb 10 '16 at 00:58
4

Keep in mind that sh actually calls dash, which is limited compared to, say, Bash.

As was observed by @Serg in a comment on the question, the curly braces are being interpreted as inputs to dash, possibly, which is why it won't work. This is why it's failing - Dash doesn't do Brace Expansion.

You're probably expecting the system to use Bash style interpretation. If you are using a default shell of bash then executing the script with ./rBackup.sh will, I believe, execute it in that running shell.


I think you're expecting that this script will be processed by Bash, which won't work when you do sh.

So let's do a couple things differently here:

(1) Define #!/bin/bash at the beginning, to make it use Bash when executed directly.

(2) chmod +x rBackup.sh - This will set the executable bit

(3) Execute the command only with ./rBackup.sh or bash ./rBackup.sh or bash rBackup.sh. This will ensure Bash is used to interpret the function in all cases; since it defines to use #!/bin/bash at the beginning of the file if you did step 1, it will try and use Bash when executing the script.

Thomas Ward
  • 74,764