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?
2 Answers 2
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:
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– JdeBP2017年08月24日 07:50:30 +00:00Commented Aug 24, 2017 at 7:50
-
thanks,you are right,i have forgotten to add the
#!/bin/bash
linefatfatson– fatfatson2017年08月24日 08:36:32 +00:00Commented Aug 24, 2017 at 8:36
sh
was beforebash
... but actually now almost on all linux distrossh
is just a symlink tobash
bash
todash
as the default shell (/bin/sh
). Dash is a smaller and faster shell that supports POSIX features but little more.ls -l /bin/sh
, nowadays it's often used as symbolic link, but name is preserved for compatibility reasonssh
is impersonated bybash
, it'sbash
running in POSIX compatibility mode (as if started with--posix
), which means different grammar.