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 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?

Paulo Tomé
  • 3,782
fatfatson
  • 159
  • 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 05: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 05: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 05:48
  • 1
    I'm not understanding the question here. Can you please give a motivating example? – Wildcard Aug 24 '17 at 05:48
  • If the script starts with #!/usr/bin/bash or #!/usr/bin/env bash then it is run with bash. This is a good practice anyway, not just with sudo. – Johan Myréen Aug 24 '17 at 05:54
  • @John1024 hehe sorry, I've been using Gentoo since a while :P but the point is that sh is kept for compatibility reasons, but now is almost never really used – Christopher Díaz Riveros Aug 24 '17 at 05:55
  • @ChristopherDíazRiveros "Almost never used"?! Any references backing that? – Kusalananda Aug 24 '17 at 06:24
  • sure @Kusalananda :) my gentoo installation,debian , ubuntu, centos, arch... all of these use /bin/sh as a symlink to another terminal as default, but I also know that freebsd still uses sh... that's way the "almost"... anyways, I think we are missing the point here, I just ment to say that sh now exists as a bin for compatibility reasons (in most distros) because historically it was before bash or dash or zsh... – Christopher Díaz Riveros Aug 24 '17 at 14:13
  • 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
  • well I learn new things everyday :) thanks for the clarification @Kusalananda, I'll have to read more in depth then – Christopher Díaz Riveros Aug 24 '17 at 14:22

2 Answers2

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:

Kusalananda
  • 333,661
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
metamorphling
  • 542
  • 1
  • 4
  • 11
  • 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 07:50
  • thanks,you are right,i have forgotten to add the #!/bin/bash line – fatfatson Aug 24 '17 at 08:36