Forward function and variables into sudo su - <user> <<EOF
sudo su -
, which is a complicated way of writing sudo -i
, constructs a pristine environment. That's the point of a login shell. Even a plain sudo
removes most variables from the environment. Furthermore sudo
is an external command; there's no way to elevate privileges in the shell script itself, only to run an external program (sudo
) with extra privileges, and that means any shell variables (i.e. non-exported variables) and functions defined in the parent shell won't be available in the child shell.
You can pass environment variables through by not invoking a login shell (sudo bash
instead of sudo su -
or sudo -i
) and configuring sudo to let these variables through (with Defaults !env_reset
or Defaults env_keep=…
in the sudoers
file). This won't help you for functions (although bash has a function export facility, sudo blocks it).
The normal way to get your functions in the child shell would be to define them there. Take care of quoting: if you use <<EOF
for the here document, the content of the here document is first expanded by the parent shell, and the result of that expansion becomes the script that the child shell sees. That is, if you write
sudo -u "$target_user" -i <<EOF
echo "$(whoami)"
EOF
this displays the name of the original user, not the target user. To avoid this first phase of expansion, quote the here document marker after the <<
operator:
sudo -u "$target_user" -i <<'EOF'
echo "$(whoami)"
EOF
So if you don't need to pass data from the parent shell to the child shell, you can use a quoted here document:
#!/bin/bash
sudo -u "$target_user" -i <<'EOF'
log_f() {
echo "LOG line: $@"
}
intVAR=$(date)
log_f "${intVAR}"
EOF
While you can make use of an unquoted here document marker to pass data from the parent shell to the child shell, this only works if the data doesn't contain any special character. That's because in a script like
sudo -u "$target_user" -i <<EOF
echo "$(whoami)"
EOF
the output of whoami
becomes a bit of shell code, not a string. For example, if the whoami
command returned "; rm -rf /; "true
then the child shell would execute the command echo ""; rm -rf /; "true"
.
If you need to pass data from the parent shell, a simple way is to pass it as arguments. Invoke the child shell explicitly and pass it positional parameters:
#!/bin/bash
extVAR="yourName"
sudo -u "$target_user" -i sh _ "$extVAR" <<'EOF'
log_f() {
echo "LOG line: $@"
}
intVAR=$(date)
log_f "${intVAR}" "${1}"
EOF
If you have multiple variables to pass, it will be more readable to pass them by name. Call env
explicitly to set environment variables for the child shell.
#!/bin/bash
extVAR="yourName"
sudo -u "$target_user" -i env extVAR="$extVAR" sh <<'EOF'
log_f() {
echo "LOG line: $@"
}
intVAR=$(date)
log_f "${intVAR}" "${1}"
EOF
Note that if you expected /etc/profile
and the target user's ~/.profile
to be read, you'll have to read them explicitly, or call bash --login
instead of sh
.