I try to use in my bashrc:
something(){
cmd="sudo $#"
...
exec {*}$cmd
}
because sudo with: eval exec "$cmd" close my session after the command, but the expansion operator {*} did not works
but the command is failed
bash: exec: {*}sudo: not found
do you have any ideas about that? And How can i keep the sudo session active after that ?
edit: how to exec a sudo command in the same shell ?
Thanks a lot
You can try like this:
something(){
cmd="sudo ${#}"
...
exec ${cmd}
}
Update :
Try using -S option with sudo to read the password from standard input, and -s option to run a shell with elevated privileges:
something() {
cmd="sudo -S -s ${#}"
...
echo "$PASSWORD" | $cmd
}
Related
I have a script (myscript.sh) which runs a few commands which need elevated privileges (i.e. needs to run with sudo).
Script is quite complex, but to demonstrate it is like below:
#!/bin/bash
echo "hello"
command1_which_needs_sudo
echo "hello2"
command2_which_needs_sudo
echo "hello3"
...
If I run it as a normal user without the required privileges:
$ ./myscript.sh
hello
must be super-user to perform this action
However if I run it with the correct privileges, it will work fine:
$ sudo ./myscript.sh
hello
hello2
hello3
Can I somehow achieve to run myscript.sh without sudo, and make the script requesting the elevated privileges only once in the beginning (and pass it back once it has finished)?
So obviously, sudo command1_which_needs_sudo will not be good, as command2 also need privileges.
How can I do this if I don't want to create another file, and due to script complexity I also don't want to do this with heredoc syntax?
If your main concern is code clarity, using wrapper functions can do a lot of good.
# call any named bash function under sudo with arbitrary arguments
run_escalated_function() {
local function_name args_q
function_name=$1; shift || return
printf -v args_q '%q ' "$#"
sudo bash -c "$(declare -f "$function_name"); $function_name $args_q"
}
privileged_bits() {
command1_which_needs_sudo
echo "hello2"
command2_which_needs_sudo
}
echo "hello"
run_escalated_function privileged_bits
echo "hello3"
If you want to run the script with root privileges without having to type sudo in the terminal nor having to type the password more than once then you can use:
#!/bin/bash
if [ "$EUID" -ne 0 ]
then
exec sudo -s "$0" "$#"
fi
echo "hello"
command1_which_needs_sudo
echo "hello2"
command2_which_needs_sudo
echo "hello3"
# ...
sudo -k
Update:
If your goal is to execute one part of the script with sudo rights then using a quoted here‑document is probably the easiest solution; there won't be any syntax issues because the current shell won't expand anything in it.
#!/bin/bash
echo "hello"
sudo -s var="hello2" <<'END_OF_SUDO'
command1_which_needs_sudo
echo "$var"
command2_which_needs_sudo
END_OF_SUDO
sudo -k
echo "hello3"
#...
remark: take notice that you can use external values in the here-document script by setting varname=value in the sudo command.
Why does the last example throw an error but the others work? Bash is invoked in any case.
#!/bin/bash
function hello {
echo "Hello! user=$USER, uid=$UID, home=$HOME";
}
# Test that it works.
hello
# ok
bash -c "$(declare -f hello); hello"
# ok
sudo su $USER bash -c "$(declare -f hello); hello"
# error: bash: -c: line 1: syntax error: unexpected end of file
sudo -i -u $USER bash -c "$(declare -f hello); hello"
It fail because of the -i or --login switch:
It seems like when debugging with -x
$ set -x
$ sudo -i -u $USER bash -c "$(declare -f hello); hello"
++ declare -f hello
+ sudo -i -u lea bash -c 'hello ()
{
echo "Hello! user=$USER, uid=$UID, home=$HOME"
}; hello'
Now if doing it manually it cause the same error:
sudo -i -u lea bash -c 'hello ()
{
echo "Hello! user=$USER, uid=$UID, home=$HOME"
}; hello'
Now lets just do a simple tiny change that make it work:
sudo -i -u lea bash -c 'hello ()
{
echo "Hello! user=$USER, uid=$UID, home=$HOME";}; hello'
The reason is that sudo -i runs everything like an interactive shell. And when doing so, every newline character from the declare -f hello is internally turned into space. The curly-brace code block need a semi-colon before the closing curly-brace when on the same line, which declare -f funcname does not provide since it expands the function source with closing curly brace at a new line.
Now lets make this behaviour very straightforward:
$ sudo bash -c 'echo hello
echo world'
hello
world
It executes both echo statements because they are separated by a newline.
but:
$ sudo -i bash -c 'echo hello
echo world'
hello echo world
It executes the first echo statement that prints everything as arguments because the newline has been replaced by a space.
It is the same code in all examples, so it should be ok.
Yes, "$(declare -f hello); hello" is always the same string. But it is processed differently by sudo su and sudo -i as found out by Lea Gris .
sudo -i quotes its arguments before passing them to bash. This quoting process seems to be undocumented and very poor. To see what was actually executed, you can print the argument passed to bash -c inside your ~/.bash_profile/:
Content of ~/.bash_profile
cat <<EOF
# is executed as
$BASH_EXECUTION_STRING
# resulting in output
EOF
Some examples of sudo -i's terrible and inconsistent quoting
Linebreaks are replaced by line continuations
$ sudo -u $USER -i echo '1
2'
# is executed as
echo 1\
2
# resulting in output
12
Quotes are escaped as literals
$ sudo -u $USER -i echo \'single\' \"double\"
# is executed as
echo \'single\' \"double\"
# resulting in output
'single' "double"
But $ is not quoted
$ sudo -u $USER -i echo \$var
# is executed as
echo $var
# resulting in output
Some side notes:
There might be a misunderstanding in your usage of su.
sudo su $USER bash -c "some command"
does not execute bash -c "echo 1; echo 2". The -c ... is interpreted by su and passed as -c ... to $USER's default shell. Afterwards, the remaining arguments are passed to that shell as well. The executed command is
defaultShellOfUSER -c "some command" bash
You probably wanted to write
sudo su -s bash -c "some command" "$USER"
Interactive shells behave differently
su just executes the command specified by -c. But sudo -i starts a login shell, in your case that login shell seems to be bash (this is not necessarily the case, see section above).
An interactive bash session behaves different from bash -c "..." or bash script.sh. An interactive bash sources files like .profile, .bash_profile, and enables history expansion, aliases, and so on. For a full list see the section Interactive Shell Behavior in bash's manual.
I want to redirect stderr of the exec command to /dev/null, like: exec hostname 2> /dev/null
for all the exec commands in my scripts.
As the very first solution I want to create an alias for that like this: alias exec='function _myexec() { exec "$#" 2> /dev/null; unset -f _myexec; }; _myexec'
but this and any other simple alias for exec just hanging.
I would appreciate your thoughts.
On my server I try run:
#!/bin/bash
PATH="/SANCFS/stats/scripts/"
for (( i=6;i<=8;i++ ));
do
echo "Running $i"
exec "/SANCFS/stats/scripts/load_cdrs.sh --debug --config /SANCFS/stats/scripts/iquall-mm4-cdr.cfg --date '2018-10-0"$i"' >> /home/stats/201810/load_cdrsIMRMM4-0"$i".ok 2>>/home/stats/201810/load_cdrsIMRMM4-0"$i".err"
done
And the result is:
cannot execute: No such file or directory
Your help, how edit/modify to run successfully ?
Here's an easier way to reproduce your problem:
$ exec "echo "hello world""
bash: exec: echo hello: not found
Running a command in bash does not require adding exec or quotes:
$ echo "hello world"
hello world
Additionally, you are using $i in single quotes in one case, and you're overwriting the shell search path PATH for seemingly no reason. Applied to your example:
#!/bin/bash
for (( i=6;i<=8;i++ ));
do
echo "Running $i"
/SANCFS/stats/scripts/load_cdrs.sh --debug --config /SANCFS/stats/scripts/iquall-mm4-cdr.cfg --date "2018-10-0$i" >> /home/stats/201810/load_cdrsIMRMM4-0"$i".ok 2>>/home/stats/201810/load_cdrsIMRMM4-0"$i".err
done
Don't use exec. That replaces the current process with the process that runs the specified command, so you won't repeat the loop. Just execute the command normally.
And the argument to exec shouldn't be all inside a single quoted string. Maybe you're confusing it with eval?
#!/bin/bash
PATH="/SANCFS/stats/scripts/"
for (( i=6;i<=8;i++ ));
do
echo "Running $i"
/SANCFS/stats/scripts/load_cdrs.sh --debug --config /SANCFS/stats/scripts/iquall-mm4-cdr.cfg --date 2018-10-0"$i" >> /home/stats/201810/load_cdrsIMRMM4-0"$i".ok 2>>/home/stats/201810/load_cdrsIMRMM4-0"$i".err
done
You could replace exec with dot ( . )
If you try the 5 options, you should see the different options
$ exec /bin/bash
$ /bin/bash
$ . /bin/bash
$ ./bin/bash
$ /bin/bash /bin/bash
I want to add lines to /etc/my.conf as "sudo" using Shell.
Without logging as sudo, I can do:
{ echo "[mysqld]"
echo "default-character-set=utf8"
echo "character_set_server=utf8"
echo "[mysql]"
echo "default-character-set=utf8"
} >> /etc/my.conf
But when:
sudo { echo "[mysqld]"
echo "default-character-set=utf8"
echo "character_set_server=utf8"
echo "[mysql]"
echo "default-character-set=utf8"
} >> /etc/my.conf
I get an error:
sudo: {: command not found
character_set_server=utf8
[mysql]
default-character-set=utf8 .....
What am I doing wrong?
Firstly, use cat with a here-doc rather than a series of echoes. It's much cleaner. You can use the special form with a dash which strips leading tabs (tabs, not spaces!) to let you indent the here-doc to make it stand out.
Secondly, you can do the redirection as sudo by using sudo sh -c to start a root subshell, in which you then run cat doing the redirection.
Putting it together:
sudo sh -c "cat >>/etc/my.conf" <<-EOF
[mysqld]
default-character-set=utf8
character_set_server=utf8
[mysql]
default-character-set=utf8
EOF
I don't know of a more direct way to write a stream to a file as root. It's a shame if there really isn't one.
An alternative to launching a new shell is to use the tee command:
{ echo "[mysqld]"
echo "default-character-set=utf8"
echo "character_set_server=utf8"
echo "[mysql]"
echo "default-character-set=utf8"
} | sudo tee -a /etc/my.conf > /dev/null
or
sudo tee -a /etc/my.conf >/dev/null <<-EOF
[mysqld]
default-character-set=utf8
character_set_server=utf8
[mysql]
default-character-set=utf8
EOF
$ sudo echo "foo
bar
baz" > f
should work
To make everything easier you should place the commands in a script, and let sudo execute that script.