7

This is working as I would expect :

$ sudo -u romain bash -c 'a="b" ; echo $a'
b
$

But with -i the variable is not echoed, why?

$ sudo -iu romain bash -c 'a="b" ; echo $a'

$

I was wondering if -i was adding a level of bash indirection or variable interpolation but if it is, how can this be working?

$ sudo -iu romain bash -c 'a="b" ; echo ${a}'
b
$
4
  • 1
    Actually, this looks like an exact dupe of Why do I need curly braces when using a variable in some versions of sudo -i? Commented Oct 18, 2022 at 10:07
  • @muru I agree it's the same basic question, but this one is focused on the behavior of $a vs ${a}, which is indeed quite surprising and not easy to figure out based on the answers of the dupe target. Since we now have a clear, specific answer for this particular case here, I think it might be better to leave it open. Commented Oct 18, 2022 at 11:00
  • yeah, the fact that sudo leaves $ not quoted and the interactions it causes are kinda surprising, and I'm not exactly sure I agree with the guy who had marked that "Why do I need curly braces..." as a duplicate of the earlier one. Oh well, guess we'd better change the duplicate list to point to this one too, then. Commented Oct 19, 2022 at 13:06
  • Related: Why does a 'sudo -i' login shell break a here-doc command string argument? Commented Oct 20, 2022 at 5:45

2 Answers 2

9

When you use -i with sudo, the user's login shell, $SHELL, will be used, and will be invoked as a login shell.

When you additionally give the command a command to run, as in

sudo -u user -i 'some-command'

... sudo will run that command with $SHELL -c, meaning it needs to convert the list of arguments it gets itself into a single command line string that gets evaluated by the shell again. For this to work, it has to escape each character in some-command, except for alphanumerics, underscores, hyphens, and dollar signs.

This means that

sudo -u user -i bash -c 'a="b" ; echo ${a}'

will be executed as the user user, escaped as the equivalent of

$SHELL -c bash\ -c\ \'a\=\"b\"\ \;\ echo\ $\{a\}\'

... while using $a turns the command into

$SHELL -c bash\ -c\ \'a\=\"b\"\ \;\ echo\ $a\'

Note that in this last command, $a is expanded by the user's login shell before it can start bash -c. In the previous command, where ${a} is used, the $\{a\} is not a valid expansion, so the user's shell makes no expansion, and the inline bash -c shell sees ${a} and can expand it.

This extra quoting that happens is explained in the sudo manual, in the section describing the -i option:

-i, --login
            Run the shell specified by the target user's password
            database entry as a login shell.  This means that login-
            specific resource files such as .profile, .bash_profile, or
            .login will be read by the shell.  If a command is specified,
            it is passed to the shell as a simple command using the -c
            option.  The command and any arguments are concatenated,
            separated by spaces, after escaping each character (including
            white space) with a backslash (‘\’) except for alphanumerics,
            underscores, hyphens, and dollar signs.  If no command is
            specified, an interactive shell is executed. [...]
2
  • Those $SHELL ... command lines look slightly off to me (and I think it might be better to look at what sudo runs in terms of the arguments, not as a command line). With sudo -u user -i bash -c 'a="b" ; echo $a', sudo gets the strings bash, -c, and a="b" ; echo $a, and escapes and joins those into bash \-c a\=\"b\"\ \;\ echo\ $a running that with $SHELL -c. (sudo doesn't see the single quotes, and doesn't add any.) There, in the command line of the middle shell is the unescaped $a. The command lines here make it appear it would expand before the middle shell runs. Commented Oct 19, 2022 at 12:48
  • (middle as in the user's login shell, as opposed to the outer shell where sudo is started, or the inner shell which is the eventual bash -c the user is actually trying to run. I don't dare make that edit though, since I'm slightly afraid I might myself be lost in the forest of backslashes here) Commented Oct 19, 2022 at 12:49
0

It's due to an odd interaction between how sudo escapes the arguments it gets on the command line to be fed to another shell (sudo -i runs the target user's login shell, while plain sudo doesn't).

In particular, it leaves the dollar sign $ not quoted, so that commands like

sudo -i /bin/echo '$USER'

work.

If it did quote the dollar sign too, that one wouldn't work, but

sudo -i sh -c 'echo "$USER"'

should. Well, I mean I think it should work, but that one runs two shells (the target user's login shell, and the sh we specifically asked for) and thinking about the escaping through that is a pain.

With $USER that example doesn't seem too useful, but you might want to run it with some other variable to check what value the user gets in their login environment.

Anyway, that's the example command used in this sudo bug report that led to what I think is still the current behaviour:

And this particular case of sudo -i sh -c 'echo $foo' being broken is discussed here:

(The latter one was linked to in comments to an earlier question about the same issue)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.