I've written a script that takes, as an argument, a string that is a concatenation of a username and a project. The script is supposed to switch (su) to the username, cd to a specific directory based upon the project string.
I basically want to do:
su $USERNAME;
cd /home/$USERNAME/$PROJECT;
svn update;
The problem is that once I do an su... it just waits there. Which makes sense since the flow of execution has passed to switching to the user. Once I exit, then the rest of the things execute but it doesn't work as desired.
I prepended su to the svn command but the command failed (i.e. it didn't update svn in the directory desired).
How do I write a script that allows the user to switch user and invoke svn (among other things)?
Much simpler: use sudo to run a shell and use a heredoc to feed it commands.
#!/usr/bin/env bash
whoami
sudo -i -u someuser bash << EOF
echo "In"
whoami
EOF
echo "Out"
whoami
(answer originally on SuperUser)
The trick is to use "sudo" command instead of "su"
You may need to add this
username1 ALL=(username2) NOPASSWD: /path/to/svn
to your /etc/sudoers file
and change your script to:
sudo -u username2 -H sh -c "cd /home/$USERNAME/$PROJECT; svn update"
Where username2 is the user you want to run the SVN command as and username1 is the user running the script.
If you need multiple users to run this script, use a %groupname instead of the username1
You need to execute all the different-user commands as their own script. If it's just one, or a few commands, then inline should work. If it's lots of commands then it's probably best to move them to their own file.
su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME"
Here is yet another approach, which was more convenient in my case (I just wanted to drop root privileges and do the rest of my script from restricted user): you can make the script restart itself from the correct user. This approach is more readable than using sudo or su -c with a "nested script". Let's suppose it is started as root initially. Then the code will look like this:
#!/bin/bash
if [ $UID -eq 0 ]; then
user=$1
dir=$2
shift 2 # if you need some other parameters
cd "$dir"
exec su "$user" "$0" -- "$#"
# nothing will be executed beyond that line,
# because exec replaces running process with the new one
fi
echo "This will be run from user $UID"
...
Use a script like the following to execute the rest or part of the script under another user:
#!/bin/sh
id
exec sudo -u transmission /bin/sh - << eof
id
eof
Use sudo instead
EDIT: As Douglas pointed out, you can not use cd in sudo since it is not an external command. You have to run the commands in a subshell to make the cd work.
sudo -u $USERNAME -H sh -c "cd ~/$PROJECT; svn update"
sudo -u $USERNAME -H cd ~/$PROJECT
sudo -u $USERNAME svn update
You may be asked to input that user's password, but only once.
It's not possible to change user within a shell script. Workarounds using sudo described in other answers are probably your best bet.
If you're mad enough to run perl scripts as root, you can do this with the $< $( $> $) variables which hold real/effective uid/gid, e.g.:
#!/usr/bin/perl -w
$user = shift;
if (!$<) {
$> = getpwnam $user;
$) = getgrnam $user;
} else {
die 'must be root to change uid';
}
system('whoami');
This worked for me
I split out my "provisioning" from my "startup".
# Configure everything else ready to run
config.vm.provision :shell, path: "provision.sh"
config.vm.provision :shell, path: "start_env.sh", run: "always"
then in my start_env.sh
#!/usr/bin/env bash
echo "Starting Server Env"
#java -jar /usr/lib/node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-2.40.0.jar &
#(cd /vagrant_projects/myproj && sudo -u vagrant -H sh -c "nohup npm install 0<&- &>/dev/null &;bower install 0<&- &>/dev/null &")
cd /vagrant_projects/myproj
nohup grunt connect:server:keepalive 0<&- &>/dev/null &
nohup apimocker -c /vagrant_projects/myproj/mock_api_data/config.json 0<&- &>/dev/null &
Inspired by the idea from #MarSoft but I changed the lines like the following:
USERNAME='desireduser'
COMMAND=$0
COMMANDARGS="$(printf " %q" "${#}")"
if [ $(whoami) != "$USERNAME" ]; then
exec sudo -E su $USERNAME -c "/usr/bin/bash -l $COMMAND $COMMANDARGS"
exit
fi
I have used sudo to allow a password less execution of the script. If you want to enter a password for the user, remove the sudo. If you do not need the environment variables, remove -E from sudo.
The /usr/bin/bash -l ensures, that the profile.d scripts are executed for an initialized environment.
I am trying to write a small script that aims to login to a remote server, load environment variables and print one of them. (In the actual script, instead of an echo, the parameters that are read are to be used. For the sake of simplicity here I am using just echo.)
The structure of the script and the commands that I tried are as follows but unfortunately none succeeds:
ssh -i lightsail.pem ubuntu#production <<< '
sudo echo $TEST_PARAMETER
sudo sh -c "~/Environment/environment-variables.sh && echo $TEST_PARAMETER"
sudo bash -c "~/Environment/environment-variables.sh && echo $TEST_PARAMETER"
sudo bash -c "source ~/Environment/environment-variables.sh && echo $TEST_PARAMETER"
sudo bash <<< "source ~/Environment/environment-variables.sh && echo $TEST_PARAMETER"
';
How environment variables can be read from a file when going into a login shell with here-string and using the sudo command?
If your environment variable is set for ubuntu and not root you will need to use sudo -E
-E Indicates to the security policy that the user wishes to preserve their existing environment variables
I'm trying to understand how to run commands from my user account in a script with root privileges. I've got this test script and the output is confusing me.
~$ cat test.sh
output:
#!/usr/bin/bash
su -c "whoami" user
su -c "echo $HOME" user
~$ sudo ./test.sh
output:
user
/root
Why does the first 'su' command seem to run as 'user' but the second seems to run as 'root'?
Double quotes expand shell variables. Try
echo su -c "echo $HOME" user
to see what I mean. It's the shell running the script that expands $HOME, which runs as root (from sudo ./test.sh). So what ends up being run is su -c 'echo /root' user.
You want
su -c 'echo $HOME' user
Here we pass echo $HOME unexpanded to su -c.
How can I run nested shell scripts with the same option? For example,
parent.sh
#!/bin/sh
./child.sh
child.sh
#!/bin/sh
ls
How can I modify parent.sh so that when I run it with sh -x parent.sh, the -x option is effective in child.sh as well and the execution of ls is displayed on my console?
I'm looking for a portable solution which is effective for rare situations such as system users with /bin/false as their registered shell. Will the $SHELL environment variable be of any help?
Clarification: I sometimes want to call parent.sh with -x, sometimes with -e, depending on the situation. So the solution must not involve hard-coding the flags.
If you use bash, i can recommend the following:
#!/bin/bash
export SHELLOPTS
./child.sh
You can propagate as many times as you need, also you can use echo $SHELLOPTS in every script down the line to see what is happening and how options are propagated if you need to understand it better.
But for /bin/sh it will fail with /bin/sh: SHELLOPTS: readonly variable because of how POSIX is enforced on /bin/sh in various systems, more info here: https://lists.gnu.org/archive/html/bug-bash/2011-10/msg00052.html
it's looks like a hack and seems it's not the best way.
But it will do exact what you want
One of the ways how you can do it - it's to create aliases to create wrappers for sh:
alias saveShell='cp /bin/sh $some_safe_place'
alias shx='cp $some_safe_place /bin/x_sh; rm /bin/sh; echo "/bin/x_sh -x $#" > /bin/sh; chmod 755 /bin/sh '
alias she='cp $some_safe_place /bin/e_sh; rm /bin/sh; echo "/bin/e_sh -e $#" > /bin/sh; chmod 755 /bin/sh '
alias restoreShell='cp $some_safe_place /bin/sh'
How to Use:
run saveShell and then use shx or she , if you would change -x on -e run restoreShell and then run shx or she
run script as usually
sh ./parent.sh
BE VERY CAREFUL WITH MOVING SH
Other solution
replace #!/bin/sh to #!/bin/sh -x or #!/bin/sh -e with sed in all sh files before running script.
I need your help in understanding this behaviour of sudo.
sudo -s -- 'ls -l' this command works but sudo 'ls -l' throws error saying
sudo: ls -l: command not found I realize it treats the entire string within quote as single command (including the spaces) but what I don't get is how does it work fine with -s flag but fails when -s is not there.
Without -s, the first argument is the name of the command to execute. With -s, the first argument is a string passed to the -c option of whatever shell ($SHELL or your system shell) is used to execute the argument.
That is, assuming $SHELL is sh, the following are equivalent:
sudo -s -- 'ls -l'
sudo -- sh -c 'ls -l'
From the sudo man page:
-s [command]
The -s (shell) option runs the shell specified by the SHELL environment variable if it is set or the shell as specified in
the password database. If a command is specified, it is passed to the
shell for execution via the shell's -c option. If no command is
specified, an interactive shell is executed.
It behaves like it does because a new shell is spawned which breaks up the words in your "quoted command" like shells do.