I have a bash script that is supposed to take some arguments and then run in a different user: test.sh

sudo su user2 <<'EOF'
echo $1

However it prints blank:

$ ./test.sh haha

I understand that it is because environment variable are reset(?). How can I pass this argument? Security wise I've heard I should not disable environment resetting. The only way comes to my mind to solve this is writing $1 to a file and then reading it back again by user2. But I guess there should be a much better way.

  • The shebang line must have an octothorpe; i.e., #!/bin/bash. – HalosGhost Sep 19 '14 at 1:31
  • ooops. # is in the file but somehow didn't end up in the copy paste. Thanks for pointing that out. – Seperman Sep 19 '14 at 1:33

If you want the entire script to run as another user, my usual technique for doing this is adding something similar to the following to the very top of the script:

if [ "$(whoami)" != "$target_user" ]; then
  exec sudo -u "$target_user" -- "$0" "$@"

Note that I use sudo here and not su. su makes it stupidly difficult to pass arguments properly, while sudo does not have this issue.

If you only want to run a small bit of code, you can alternatively do something such as:

sudo -u "$target_user" sh -s "$@" <<'EOF'
  echo "$@"

This will launch sh, pass it the current script's arguments, and then execute the script provided via the heredoc.

  • 1
    your method works pretty well. However I went with the simpler method that G-Man recommended which is not to quote EOF. If you believe your method is for some reason better i.e. security wise or flexibility, I would like to know why please. Thanks – Seperman Sep 19 '14 at 4:45

By putting the EOF in quotes, you are effectively quoting the "here document" (echo $1), such that $1 is interpreted by the user2 shell.  But that shell doesn't have any positional parameters.  I can't test these right now, but here are a couple of approaches that might work:

  • Don't quote EOF:

    sudo su user2 << EOF
    echo $1
  • Pass values through the environment:

    export my_val="$1"
    sudo su user2 << 'EOF'
    echo "$my_val"
  • sudo su. WHYYYYY? – phemmer Sep 19 '14 at 3:14
  • Not quoting EOF worked. – Seperman Sep 19 '14 at 4:41
  • @Erasmose Not quoting EOF works only in simple cases. If the argument contains characters that are interpreted by the shell, they'll be parsed again by the inner shell. For example, try myscript '; this_could_be_rm -rf /' – Gilles 'SO- stop being evil' Sep 19 '14 at 21:27
  • Using the environment only works if sudo doesn't strip the variable. A variable name beginning with LC_ is more likely to go through. – Gilles 'SO- stop being evil' Sep 19 '14 at 21:29

This might work :

su - user2 -c 'echo "$0" "$@"' -- "$@"

Use simple quotes ' to pass the command argument to su so you don't have to escape the double quotes ".

References :

Escaping bash function arguments for use by su -c - Stack Overflow

bash - Passing arguments to su-provided shell - Unix & Linux Stack Exchange

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.