9

I have declared functions and variables in bash/ksh and I need to forward them into sudo su - {user} << EOF:

#!/bin/bash

log_f() {
echo "LOG line: $@"
}

extVAR="yourName"

sudo su - <user> << EOF
  intVAR=$(date)
  log_f ${intVAR} ${extVAR}
EOF
4

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.

1

This doesn't work because the function log_f is not declared in the sudo su - shell you launch. Instead:

extVAR="yourName"

sudo su - <user> << EOF

log_f() {
echo "LOG line: $@"
}

  intVAR=$(date)
  log_f ${intVAR} ${extVAR}
EOF

You need to get the function defined in the root subshell. That might do it, but.... I don't know what most of that stuff does. At least - so long as neither sudo nor su needs stdin to read a password - that should get log_f() declared.

I trust that you mean to expand those values into the root shell's input, by the way. If you do not mean to do so then you should quote EOF and the vars themselves.

1

In my case I needed to pass an array in and had some trouble, but had success after a while by echoing the array's values to an env-key and wrapping the intended code in bash -c '<intended code>', and basically have it recreating the array, eg.:INNER_KEY=($<env_key>).

For examlpe:

#!/usr/bin/env bash
PLUGINS=(foo bar baz)
sudo -u <some-user> -i env PLUGINS="`echo ${PLUGINS[@]}`" sh <<'EOF'
  bash -c '
    FOO=($PLUGINS);
    echo values: \[${FOO[@]}\];

    for pl in ${FOO[@]};
      do echo value: $pl;
    done;
  '
EOF

The problem was that I could not directly do something like the following (not using bash -c '...'):

FOO=($PLUGINS);
for pl in ${FOO[@]};
...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.