Execute a shell script in current shell with sudo permission
Asked Answered
F

11

96

To execute a shell script in current shell, we need to use a period . or a source command. But why does it not work with a sudo permission?

I have a script with execute permission called setup.sh. When I use a period, I get this:

$ sudo . ./setup.sh 
sudo: .: command not found

The source command also produces a similar error. Am I missing out something? What should I do to run the script with sudo permission in the same/current shell?

Finding answered 15/9, 2013 at 6:38 Comment(4)
Despite the excellent answers, I think the problem is almost just a typo. Delete the first period that is by itself.Outspan
@Outspan you are in error. The problem is not a typo. If setup.sh does not have execute privileges, then you must explicitly interpret it with bash. This can be done as bash setup.sh or source setup.sh or . setup.sh. However, because the latter 2 are bash built-ins, only the 1st form can be used with sudo which requires an executable. You can use sudo which bash and sudo which source and sudo which . to see what sudo will find.Candescent
@BrunoBronosky it looked (looks) to me like they are trying to run the first lone dot as a command. Since this specifies the current working directory, which is not a command, it throws that error. If they put ./setup.sh it would be a different story.Outspan
@Outspan A lone dot is a shell command -- specifically, it's a shell builtin that's equivalent to the source command (also a builtin).Saadi
S
49

What you are trying to do is impossible; your current shell is running under your regular user ID (i.e. without root the access sudo would give you), and there is no way to grant it root access. What sudo does is create a new *sub*process that runs as root. The subprocess could be just a regular program (e.g. sudo cp ... runs the cp program in a root process) or it could be a root subshell, but it cannot be the current shell.

(It's actually even more impossible than that, because the sudo command itself is executed as a subprocess of the current shell -- meaning that in a sense it's already too late for it to do anything in the "current shell", because that's not where it executes.)

Saadi answered 15/9, 2013 at 17:35 Comment(3)
I'm not sure why this is the accepted answer when @JaseC's answer below seems to work for me. sudo bash myscript.sh does exactly what I was looking for, and what the OP seemed to be asking for.Indochina
@dalesikkema: sudo bash myscript.sh will run the script in a subshell, rather than the current shell. If that works in your situation, great -- but this particular question is about running the script as root in the current shell.Saadi
The entire premise of ambiguous because of the inaccurate word choice. "For executing a shell script in current shell, we need to use a period" No, that period doesn't execute, it sources, inserts, reads, or interprets. Execution implies a disparate PID. So, we are unclear if/why they might care about the PID of their current shell.Candescent
C
184

I'm not sure if this breaks any rules but

sudo bash script.sh

seems to work for me.

Cesium answered 7/5, 2014 at 0:50 Comment(1)
This doesn't run the script in the same/current shell, like source or . would. It runs a new bash shell as a subprocess of sudo (which is itself run as a subprocess of the current shell). This is fine for most scripts (it's the normal way to run scripts), but for anything that must run in the current shell it doesn't do it.Saadi
S
49

What you are trying to do is impossible; your current shell is running under your regular user ID (i.e. without root the access sudo would give you), and there is no way to grant it root access. What sudo does is create a new *sub*process that runs as root. The subprocess could be just a regular program (e.g. sudo cp ... runs the cp program in a root process) or it could be a root subshell, but it cannot be the current shell.

(It's actually even more impossible than that, because the sudo command itself is executed as a subprocess of the current shell -- meaning that in a sense it's already too late for it to do anything in the "current shell", because that's not where it executes.)

Saadi answered 15/9, 2013 at 17:35 Comment(3)
I'm not sure why this is the accepted answer when @JaseC's answer below seems to work for me. sudo bash myscript.sh does exactly what I was looking for, and what the OP seemed to be asking for.Indochina
@dalesikkema: sudo bash myscript.sh will run the script in a subshell, rather than the current shell. If that works in your situation, great -- but this particular question is about running the script as root in the current shell.Saadi
The entire premise of ambiguous because of the inaccurate word choice. "For executing a shell script in current shell, we need to use a period" No, that period doesn't execute, it sources, inserts, reads, or interprets. Execution implies a disparate PID. So, we are unclear if/why they might care about the PID of their current shell.Candescent
D
44

I think you are confused about the difference between sourcing and executing a script.

Executing a script means creating a new process, and running the program. The program can be a shell script, or any other type of program. As it is a sub process, any environmental variables changed in the program will not affect the shell.

Sourcing a script can only be used with a bash script (if you are running bash). It effectively types the commands in as if you did them. This is useful as it lets a script change environmental variables in the shell.


Running a script is simple, you just type in the path to the script. . is the current directory. So ./script.sh will execute the file script.sh in the current directory. If the command is a single file (eg script.sh), it will check all the folders in the PATH variable to find the script. Note that the current directory isn't in PATH, so you can't execute a file script.sh in the current directory by running script.sh, you need to run ./script.sh (unless the current directory is in the PATH, eg you can run ls while in the /bin dir).

Sourcing a script doesn't use the PATH, and just searches for the path. Note that source isn't a program - otherwise it wouldn't be able to change environmental variables in the current shell. It is actually a bash built in command. Search /bin and /usr/bin - you won't find a source program there. So to source a file script.sh in the current directory, you just use source script.sh.


How does sudo interact with this? Well sudo takes a program, and executes it as root. Eg sudo ./script.sh executes script.sh in a sub process but running as root.

What does sudo source ./script.sh do however? Remember source isn't a program (rather a shell builtin)? Sudo expects a program name though, so it searches for a program named source. It doesn't find one, and so fails. It isn't possible to source a file running as root, without creating a new subprocess, as you cannot change the runner of a program (in this case, bash) after it has started.

I'm not sure what you actually wanted, but hopefully this will clear it up for you.


Here is a concrete example. Make the file script.sh in your current directory with the contents:

#!/bin/bash    
export NEW_VAR="hello"
whoami
echo "Some text"

Make it executable with chmod +x script.sh.

Now observe what happens with bash:

> ./script.sh
david
Some text
> echo $NEW_VAR

> sudo ./script.sh
root
Some text
> echo $NEW_VAR

> source script.sh
david
Some text
> echo $NEW_VAR
hello
> sudo source script.sh
sudo: source: command not found
Darksome answered 15/9, 2013 at 7:2 Comment(1)
I think this is the best answer to the OP question, making the OP understand that 1) there's no real reason to source a script with sudo, and 2) that 'sudo source" is equivalent to 'sudo .' where both source and . are builtins and therefore applying sudo to a builtin will fail.Gadoid
N
5

Basically sudo expects, an executable (command) to follow & you are providing with a .

Hence the error.

Try this way $ sudo setup.sh


Nacreous answered 15/9, 2013 at 6:41 Comment(1)
While the info in the answer is correct, it misses explaining to the OP the difference between executing a script and sourcing a script.Gadoid
C
3

If you really want to "ExecuteCall a shell script in current shell with sudo permission" you can use exec to...

replace the shell with a given program (executing it, not as new process)

I insist on replacing "execute" with "call" because the former has a meaning that includes creating a new process and ID, where the latter is ambiguous and leaves room for creativity, of which I am full.

Consider this test case and look closely at pid 1337

# Don't worry, the content of this script is cat'ed below
$ ./test.sh -o foo -p bar

User ubuntu is running...
 PID TT       USER     COMMAND
 775 pts/1    ubuntu   -bash
1408 pts/1    ubuntu    \_ bash ./test.sh -o foo -p bar
1411 pts/1    ubuntu        \_ ps -t /dev/pts/1 -fo pid,tty,user,args

User root is running...
 PID TT       USER     COMMAND
 775 pts/1    ubuntu   -bash
1337 pts/1    root      \_ sudo ./test.sh -o foo -p bar
1412 pts/1    root          \_ bash ./test.sh -o foo -p bar
1415 pts/1    root              \_ ps -t /dev/pts/1 -fo pid,tty,user,args

Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)

#!/usr/bin/env bash

echo; echo "User $(whoami) is running..."
ps -t $(tty) -fo pid,tty,user,args

if [[ $EUID > 0 ]]; then
    # exec replaces the current process effectively ending execution so no exit is needed.
    exec sudo "$0" "$@"
fi

echo; echo "Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)"; echo
cat $0

Here is another test using sudo -s

$ ps -fo pid,tty,user,args; ./test2.sh
  PID TT       USER     COMMAND
10775 pts/1    ubuntu   -bash
11496 pts/1    ubuntu    \_ ps -fo pid,tty,user,args

User ubuntu is running...
  PID TT       USER     COMMAND
10775 pts/1    ubuntu   -bash
11497 pts/1    ubuntu    \_ bash ./test2.sh
11500 pts/1    ubuntu        \_ ps -fo pid,tty,user,args

User root is running...
  PID TT       USER     COMMAND
11497 pts/1    root     sudo -s
11501 pts/1    root      \_ /bin/bash
11503 pts/1    root          \_ ps -fo pid,tty,user,args

$ cat test2.src
echo; echo "User $(whoami) is running..."
ps -fo pid,tty,user,args

$ cat test2.sh
#!/usr/bin/env bash

source test2.src

exec sudo -s < test2.src

And a simpler test using sudo -s

$ ./exec.sh
bash's PID:25194    user ID:7809
systemd(1)───bash(23064)───bash(25194)───pstree(25196)

Finally...
bash's PID:25199    user ID:0
systemd(1)───bash(23064)───sudo(25194)───bash(25199)───pstree(25201)

$ cat exec.sh
#!/usr/bin/env bash

pid=$$
id=$(id -u)
echo "bash's PID:$pid    user ID:$id"
pstree -ps $pid

# the quoted EOF is important to prevent shell expansion of the $...
exec sudo -s <<EOF
echo
echo "Finally..."
echo "bash's PID:\$\$    user ID:\$(id -u)"
pstree -ps $pid
EOF
Candescent answered 21/1, 2017 at 0:30 Comment(2)
I got a down vote today with no comment. How is that helpful to anyone? Enjoy losing your 2 reputation points. (That's right. It takes your reputation down 2 points in order to give me 1 down vote.)Candescent
a bit confusing.... Next time, put the commands and output plain straigh, instead of describing what you did. It would help explain commands used inside your script, not everyone has the same proeficiency that you do.Dirty
S
3

It works without "sudo".

bash setup.sh
Sinuation answered 2/3, 2021 at 0:26 Comment(0)
D
2

Even the first answer is absolutely brilliant, you probably want to only run script under sudo.

You have to specify the absolute path like:

sudo /home/user/example.sh
sudo ~/example.sh

(both are working)

THIS WONT WORK!

sudo /bin/sh example.sh
sudo example.sh

It will always return

sudo: bin/sh: command not found
sudo: example.sh: command not found
Dygal answered 10/1, 2019 at 17:49 Comment(0)
D
1

The answers here explain why it happens but I thought I'd add my simple way around the issue. First you can cat the file into a variable with sudo permissions. Then you can evaluate the variable to execute the code in the file in your current shell.

Here is an example of reading and executing an .env file (ex Docker)

 sensitive_stuff=$(sudo cat ".env")
 eval "${sensitive_stuff}"
 echo $ADMIN_PASSWORD 
Downswing answered 31/12, 2019 at 15:9 Comment(0)
Y
0

Easiest method is to type:

sudo /bin/sh example.sh
Yun answered 26/5, 2017 at 0:35 Comment(1)
no "cannot open example.sh no such file" even when it's in the same directory.Susannasusannah
R
0

sudo -s Should do the trick

example where script must be run as normal user and then execute commands as sudo (no pass)

ubuntu@xoan:~/scripts$ bash rp_filter.sh
#!/bin/bash
juju ssh 9 sudo -s sysctl -w net.ipv4.conf.all.rp_filter=0 && sysctl -p
ubuntu@xoan:~/scripts$ bash rp_filter.sh
net.ipv4.conf.all.rp_filter = 0 (<- confirms set loose)

Connection to 192.168.1.189 closed.
Rounder answered 1/6, 2023 at 4:14 Comment(0)
C
-1
sudo bash -c $(cat ./setup.sh)
Coxcombry answered 24/2 at 23:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.