2

according to sudo(8):

Process model When sudo runs a command, it calls fork(2), sets up the execution environment as described above, and calls the execve system call in the child process.

Also I have found that that the forked child process execs its command using sh.

So if the command is a bash script with some bash-specific command such as source in it, the sh will not exec it correctly. For example:

% cat /tmp/wibble
source something
% ls -l /tmp/wibble
-rwxr-xr-x 1 user user 17 Aug 24 08:32 /tmp/wibble
% getent passwd root
root:x:0:0:root:/root:/bin/bash
% /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% /bin/bash /tmp/wibble                                                                                                                                                                                                      ~ [pts/3.4028.1]
/tmp/wibble: line 1: something: No such file or directory
% /bin/dash /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% /bin/sh /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% echo $SHELL
/bin/zsh
% sudo /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% sudo -s /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% sudo -i /tmp/wibble
/tmp/wibble: line 1: something: No such file or directory
% export SHELL=/bin/bash
% sudo /tmp/wibble
/tmp/wibble: 1: /tmp/wibble: source: not found
% sudo -s /tmp/wibble
/tmp/wibble: line 1: something: No such file or directory
% sudo -i /tmp/wibble
/tmp/wibble: line 1: something: No such file or directory
%

Often we can append a -s option to sudo to solve this problem, as in the aforegiven example, but I'd like to know why sudo uses sh as default. Is it so that it can be configured to other shells?

  • this may be an historical answer... since sh was before bash... but actually now almost on all linux distros sh is just a symlink to bash – Christopher Díaz Riveros Aug 24 '17 at 5:28
  • 2
    @ChristopherDíazRiveros Some years ago, Debian and Ubuntu and their many derivatives switched from bash to dash as the default shell (/bin/sh). Dash is a smaller and faster shell that supports POSIX features but little more. – John1024 Aug 24 '17 at 5:42
  • You can confirm what /bin/sh actually points to by typing ls -l /bin/sh, nowadays it's often used as symbolic link, but name is preserved for compatibility reasons – metamorphling Aug 24 '17 at 5:48
  • 1
    I'm not understanding the question here. Can you please give a motivating example? – Wildcard Aug 24 '17 at 5:48
  • 1
    @ChristopherDíazRiveros Even on Unices where sh is impersonated by bash, it's bash running in POSIX compatibility mode (as if started with --posix), which means different grammar. – Kusalananda Aug 24 '17 at 14:17
3

Make sure that your script starts with the appropriate #!-line.

If a script is executable and starts with, e.g.,

#!/bin/bash

(or whatever the path is for bash on your system), then the script will be interpreted by /bin/bash if you typed sudo script, just like a Python script would get interpreted by /usr/bin/python if its first line was #!/usr/bin/python.

The question whether sh is bash or some other shell is uninteresting here. In general, assume that each type of shell is its own mutually exclusive scripting language and use the #!-line to specify exactly what interpreter you wrote the script for. If you, for example, write a portable script only using POSIX grammar and syntax, use #!/bin/sh, but use #!/bin/bash if you use bash's arrays or other things that this shell extends the POSIX shell with.

When a text file doesn't start with #!, /bin/sh is used as a fallback. This has nothing to do with sudo.

In your updated question you show a number of invocations of a script with no #!-line. They all fail either because the interpreter does not understand the source command (source: not found) or because it can't find the file (something: No such file or directory).

To summarize: Always specify the interpreter for scripts using a #!-line.

Additionally: When using source, do specify the path to the file to source, even if it's just ./something, otherwise it may be picked up from somewhere in your $PATH.

Related:

0

UPDATE: question has been heavily modified, so have to be my answer.
While I found no standard that defines what is considered to be a "script", then probably even just a file with list of commands is a "script" as long as it is being executed by shell. So, one should be careful, that if no shell specified at the top of the script, default behaviour(sh) will be assumed.
OLD ANSWER:
If sh is not bash on the current system, script should fail because bashisms won't be understood.
Let's do an experiment!

~/Downloads/test$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4  8月 31  2016 /bin/sh -> dash

First things first, confirm that sh is not bash.
bash.sh is going to utilize bash. It calls another bash script, using sudo.

:~/Downloads/test$ cat bash.sh 
#!/bin/bash

sudo ./sudo_bash.sh

dash.sh is a dash script, this it to show that ((i++)) bashism should fail according to your words.

:~/Downloads/test$ cat dash.sh 
#!/bin/sh

i=0
((i++))
echo $i

Let's confirm it

:~/Downloads/test$ ./dash.sh 
./dash.sh: 4: ./dash.sh: i++: not found
0
:~/Downloads/test$ 

Beautifully failed. Now, sudo_bash.sh is a bash script. We will execute it from bash.sh with sudo command, and as you said, if it really is forked with sh then it should fail too.

:~/Downloads/test$ cat sudo_bash.sh 
#!/bin/bash

i=0
((i++))
echo $i

:~/Downloads/test$ ./bash.sh 
[sudo] password for cs-server: 
1
:~/Downloads/test$

Executed successfully. That proves that the new environment does not use /bin/sh.

I'd like to know why sudo uses sh as default.  

Historically first shell was sh, so name is still kept for compatibility reasons.

Is it so that it can be configured to other shells?

Sure, just change the symlink of your sh

:~/Downloads/test$ cat text_file 
i=0
((i++))
echo $i
:~/Downloads/test$ chmod +x text_file 
:~/Downloads/test$ sudo ln -sf bash /bin/sh
:~/Downloads/test$ sudo ./text_file
1
:~/Downloads/test$ sudo ln -sf dash /bin/sh
:~/Downloads/test$ sudo ./text_file
./text_file: 2: ./text_file: i++: not found
0
  • You're making an assumption of something that the original question did not in fact state. Remove your assumption, in the way that the question now makes explicit, and things very much do behave as the questioner said. – JdeBP Aug 24 '17 at 7:50
  • thanks,you are right,i have forgotten to add the #!/bin/bash line – fatfatson Aug 24 '17 at 8:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.