“sh -c” does not expand positional parameters, if I run it from “sudo --login”. Is there a way around this?Change directory and then chain (`exec`) a command?How to use SCSUDO to run scripts with positional parameters?How to run this in sudo?Set sudo password differently from login oneIs there a way to run sudo without running another unneccessary command?How to read from a pipe keeping positional parameters?Why can I run `sudo bash` but not `sudo su`?Is there a way to get the positional parameters of the script from inside a function in bash?Why does sudo accept password not from stdin by default?
Journal published a paper, ignoring my objections as a referee
Why is there not a willingness from the world to step in between Pakistan and India?
What checks exist against overuse of presidential pardons in the USA?
Wrong Stamping of UK Visa
Are sweatpants frowned upon on flights?
Why didn't Doc believe Marty was from the future?
Looking for a plural noun related to ‘fulcrum’ or ‘pivot’ that denotes multiple things as crucial to success
How do you say "half the time …, the other half …" in German?
What is Soda Fountain Etiquette?
Employing a contractor proving difficult
Do multi-engine jets need all engines with equal age to reduce asymmetry in thrust and fuel consumption arising out of deterioration?
Get contents before a colon
Another "Ask One Question" Question
How do I portray irrational anger in first person?
In Endgame, wouldn't Stark have remembered Hulk busting out of the stairwell?
Is the Amazon rainforest the "world's lungs"?
Is there a word or phrase that means "use other people's wifi or Internet service without consent"?
Why can't you say don't instead of won't?
How did medieval manors handle population growth? Was there room for more fields to be ploughed?
Term used to describe a person who predicts future outcomes
Why does Sauron not permit his followers to use his name?
Coupling two 15 Amp circuit breaker for 20 Amp
Why does AM radio react to IR remote?
I feel cheated on by my new employer, does this sound right?
“sh -c” does not expand positional parameters, if I run it from “sudo --login”. Is there a way around this?
Change directory and then chain (`exec`) a command?How to use SCSUDO to run scripts with positional parameters?How to run this in sudo?Set sudo password differently from login oneIs there a way to run sudo without running another unneccessary command?How to read from a pipe keeping positional parameters?Why can I run `sudo bash` but not `sudo su`?Is there a way to get the positional parameters of the script from inside a function in bash?Why does sudo accept password not from stdin by default?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I wrote an "inline script" which uses positional parameters, e.g.
$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
I actually want to run my inline script using sudo --login
. But then the positional parameters don't work. Is there a way to make them work?
(I'm also interested if the behaviour is documented or standardized somewhere).
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2
Software versions:
$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
shell-script sudo
add a comment |
I wrote an "inline script" which uses positional parameters, e.g.
$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
I actually want to run my inline script using sudo --login
. But then the positional parameters don't work. Is there a way to make them work?
(I'm also interested if the behaviour is documented or standardized somewhere).
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2
Software versions:
$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
shell-script sudo
1
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped inbash -c
. Wrap your own command inbash -c
and maybe some mess of quoting might work.
– muru
Aug 16 at 18:58
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I usedstrace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to callbash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in"$@"
, but not$1
or$2
. (And then I checked further; they are also not in$3
nor$4
nor$5
nor$6
).
– sourcejedi
Aug 16 at 19:16
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30
add a comment |
I wrote an "inline script" which uses positional parameters, e.g.
$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
I actually want to run my inline script using sudo --login
. But then the positional parameters don't work. Is there a way to make them work?
(I'm also interested if the behaviour is documented or standardized somewhere).
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2
Software versions:
$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
shell-script sudo
I wrote an "inline script" which uses positional parameters, e.g.
$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
I actually want to run my inline script using sudo --login
. But then the positional parameters don't work. Is there a way to make them work?
(I'm also interested if the behaviour is documented or standardized somewhere).
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2
Software versions:
$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
shell-script sudo
shell-script sudo
asked Aug 16 at 18:37
sourcejedisourcejedi
29.4k5 gold badges52 silver badges131 bronze badges
29.4k5 gold badges52 silver badges131 bronze badges
1
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped inbash -c
. Wrap your own command inbash -c
and maybe some mess of quoting might work.
– muru
Aug 16 at 18:58
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I usedstrace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to callbash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in"$@"
, but not$1
or$2
. (And then I checked further; they are also not in$3
nor$4
nor$5
nor$6
).
– sourcejedi
Aug 16 at 19:16
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30
add a comment |
1
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped inbash -c
. Wrap your own command inbash -c
and maybe some mess of quoting might work.
– muru
Aug 16 at 18:58
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I usedstrace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to callbash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in"$@"
, but not$1
or$2
. (And then I checked further; they are also not in$3
nor$4
nor$5
nor$6
).
– sourcejedi
Aug 16 at 19:16
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30
1
1
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped in bash -c
. Wrap your own command in bash -c
and maybe some mess of quoting might work.– muru
Aug 16 at 18:58
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped in bash -c
. Wrap your own command in bash -c
and maybe some mess of quoting might work.– muru
Aug 16 at 18:58
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I used
strace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to call bash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in "$@"
, but not $1
or $2
. (And then I checked further; they are also not in $3
nor $4
nor $5
nor $6
).– sourcejedi
Aug 16 at 19:16
@muru I used
strace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to call bash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in "$@"
, but not $1
or $2
. (And then I checked further; they are also not in $3
nor $4
nor $5
nor $6
).– sourcejedi
Aug 16 at 19:16
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30
add a comment |
1 Answer
1
active
oldest
votes
For the same reason
sudo --login echo '$HOME'
Doesn't output $HOME
, but the content of the $HOME
variable (of the target user, as set by sudo
).
In your case, it's not because sh -c doesn't expand the positional parameters, but because the $0
, $1
, $2
were already expanded (by the login shell) by the time sh
was started.
What is more surprising here is why you see all=1 2
. And it's the same reason why you see $@
upon sudo --login echo '$@'
.
Both -s
/--shell
and -i
/--login
run a shell to run the command. But sudo
does it in a very strange way.
I'd expect sudo -s 'some shell code'
to run a shell to interpret some shell code
, but that's not what it does. With -s
, it still kind-of expects to run a single simple command, and tries to quote (with ) some characters in the hope of making it happen through that shell.
You're not meant to do sudo -s 'echo test'
, but sudo -s echo test
. In the first case, sudo actually passes echo test
as shell code. In Bourne-like shells, that tries to run a command called 'echo test'
. With a rc
shell, that would run a command called echo
with test
as argument.
Beside SPC, there are a few characters that sudo
escapes. That includes backtick, ;
, |
, (
, )
, *
, &
, =
, but strangely enough, not
$
(or maybe that's the only raison d'être of those -s
/--login
options: to have a shell expand variables).
It does include @
though. If you look at the code, it escapes all bytes except the ASCII alnums, _
, -
and $
.
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
So:
sudo -s echo '$@'
sudo
actually runs "$SHELL" -c 'echo $@'
and that explains why in your example, $@
is not expanded by the shell started by sudo --login
but by the one you tell it to run.
In your
sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
sudo
runs root
's login shell (bash
in your case) and tells it to interpret:
sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
The arguments have been joined with spaces and all the SPC, "
, =
, &
, @
characters, but not $
have been escaped. Because those $
have not been escaped that bash login shell will expand $0,
$1, $2
but not $@
because of that backslash.
You can see it with:
$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
So that login bash
shell ends up running sh
with -c
as first argument,
echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"
as second argument and sh
, 1
, 2
as 3rd, 4th and 5th.
To work around it, you can use $0
instead of $0
, because sudo
transforms it to $0
which prevents the login shell from expanding it to the content of its $0
:
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
Edit: history brings light to the reason behind it
more findings while looking at the code. The non-escaping of $
was apparently introduced in 2013 by this change. Which refers to bug#564 which refers to bug#413.
It looks like before that bug#413 was "fixed", sudo
behaved as I expected.
That is:
sudo -i 'some shell code'
Had the login shell interpret that shell code (and sudo -s 'some shell code'
had $SHELL
interpret that shell code). But the bug#413 resolution broke it, because the person who reported the bug didn't understand it worked that way. And the bug#564 broke it further (in an attempt to revert only part of the breakage introduced by the bug#413 resolution).
I went as far as compiling sudo
1.7.1 from 2009 and the -s
/-i
options were working as I'd expect then. 1.7.3
(with bug#413 resolution) worked kind of like you apparently expected:
$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
That escaping introduced in the fix for that bug#413 is easily fooled because banging a in front of all bytes (not character) doesn't work for all.
Beside the obvious case of rc
where is not even a quoting operator, in most shells, newline can't be quoted with
:
$ SHELL=sh sudo -s echo $'anb'
ab
$ SHELL=csh sudo -s echo $'anb'
a b
It also means empty arguments are discarded:
$ sudo printf '<%s>n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>n' a '' b
<a>
<b>
Also, by inserting a before every byte, it's transforming characters into others in charsets where byte 0x5c (the encoding of
) is found in other characters:
$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'xa3``uname`xa3`'
bash: line 2: Linuxα: command not found
α
0xa3 0x60 is Greek Epsilon in that locale. And sudo changes the 0x60s to 0x5c 0x60 and 0xa3 0x5c is Greek Alpha, which explains why the uname
command was run. sudo
changed the
ε`uname`ε
to
α``uname`α`
And of course the behaviour ends up being surprising in that the $-
, $$
, positional parameters and $varname
(where varname
is a valid POSIX variable name) are expanded but not the other parameters (like $@
here, but also $!
, $?
, $*
, $#
) or $varname
or $var[1]
(csh
/tcsh
/zsh
) or $var(1)
(rc
/es
/...).
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee thatsudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".
– sourcejedi
Aug 16 at 20:30
1
@Kusalananda, I never understood why sudo handled-s
/--login
that way. To me, it makes it pointless.
– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f535925%2fsh-c-does-not-expand-positional-parameters-if-i-run-it-from-sudo-login%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
For the same reason
sudo --login echo '$HOME'
Doesn't output $HOME
, but the content of the $HOME
variable (of the target user, as set by sudo
).
In your case, it's not because sh -c doesn't expand the positional parameters, but because the $0
, $1
, $2
were already expanded (by the login shell) by the time sh
was started.
What is more surprising here is why you see all=1 2
. And it's the same reason why you see $@
upon sudo --login echo '$@'
.
Both -s
/--shell
and -i
/--login
run a shell to run the command. But sudo
does it in a very strange way.
I'd expect sudo -s 'some shell code'
to run a shell to interpret some shell code
, but that's not what it does. With -s
, it still kind-of expects to run a single simple command, and tries to quote (with ) some characters in the hope of making it happen through that shell.
You're not meant to do sudo -s 'echo test'
, but sudo -s echo test
. In the first case, sudo actually passes echo test
as shell code. In Bourne-like shells, that tries to run a command called 'echo test'
. With a rc
shell, that would run a command called echo
with test
as argument.
Beside SPC, there are a few characters that sudo
escapes. That includes backtick, ;
, |
, (
, )
, *
, &
, =
, but strangely enough, not
$
(or maybe that's the only raison d'être of those -s
/--login
options: to have a shell expand variables).
It does include @
though. If you look at the code, it escapes all bytes except the ASCII alnums, _
, -
and $
.
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
So:
sudo -s echo '$@'
sudo
actually runs "$SHELL" -c 'echo $@'
and that explains why in your example, $@
is not expanded by the shell started by sudo --login
but by the one you tell it to run.
In your
sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
sudo
runs root
's login shell (bash
in your case) and tells it to interpret:
sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
The arguments have been joined with spaces and all the SPC, "
, =
, &
, @
characters, but not $
have been escaped. Because those $
have not been escaped that bash login shell will expand $0,
$1, $2
but not $@
because of that backslash.
You can see it with:
$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
So that login bash
shell ends up running sh
with -c
as first argument,
echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"
as second argument and sh
, 1
, 2
as 3rd, 4th and 5th.
To work around it, you can use $0
instead of $0
, because sudo
transforms it to $0
which prevents the login shell from expanding it to the content of its $0
:
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
Edit: history brings light to the reason behind it
more findings while looking at the code. The non-escaping of $
was apparently introduced in 2013 by this change. Which refers to bug#564 which refers to bug#413.
It looks like before that bug#413 was "fixed", sudo
behaved as I expected.
That is:
sudo -i 'some shell code'
Had the login shell interpret that shell code (and sudo -s 'some shell code'
had $SHELL
interpret that shell code). But the bug#413 resolution broke it, because the person who reported the bug didn't understand it worked that way. And the bug#564 broke it further (in an attempt to revert only part of the breakage introduced by the bug#413 resolution).
I went as far as compiling sudo
1.7.1 from 2009 and the -s
/-i
options were working as I'd expect then. 1.7.3
(with bug#413 resolution) worked kind of like you apparently expected:
$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
That escaping introduced in the fix for that bug#413 is easily fooled because banging a in front of all bytes (not character) doesn't work for all.
Beside the obvious case of rc
where is not even a quoting operator, in most shells, newline can't be quoted with
:
$ SHELL=sh sudo -s echo $'anb'
ab
$ SHELL=csh sudo -s echo $'anb'
a b
It also means empty arguments are discarded:
$ sudo printf '<%s>n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>n' a '' b
<a>
<b>
Also, by inserting a before every byte, it's transforming characters into others in charsets where byte 0x5c (the encoding of
) is found in other characters:
$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'xa3``uname`xa3`'
bash: line 2: Linuxα: command not found
α
0xa3 0x60 is Greek Epsilon in that locale. And sudo changes the 0x60s to 0x5c 0x60 and 0xa3 0x5c is Greek Alpha, which explains why the uname
command was run. sudo
changed the
ε`uname`ε
to
α``uname`α`
And of course the behaviour ends up being surprising in that the $-
, $$
, positional parameters and $varname
(where varname
is a valid POSIX variable name) are expanded but not the other parameters (like $@
here, but also $!
, $?
, $*
, $#
) or $varname
or $var[1]
(csh
/tcsh
/zsh
) or $var(1)
(rc
/es
/...).
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee thatsudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".
– sourcejedi
Aug 16 at 20:30
1
@Kusalananda, I never understood why sudo handled-s
/--login
that way. To me, it makes it pointless.
– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
add a comment |
For the same reason
sudo --login echo '$HOME'
Doesn't output $HOME
, but the content of the $HOME
variable (of the target user, as set by sudo
).
In your case, it's not because sh -c doesn't expand the positional parameters, but because the $0
, $1
, $2
were already expanded (by the login shell) by the time sh
was started.
What is more surprising here is why you see all=1 2
. And it's the same reason why you see $@
upon sudo --login echo '$@'
.
Both -s
/--shell
and -i
/--login
run a shell to run the command. But sudo
does it in a very strange way.
I'd expect sudo -s 'some shell code'
to run a shell to interpret some shell code
, but that's not what it does. With -s
, it still kind-of expects to run a single simple command, and tries to quote (with ) some characters in the hope of making it happen through that shell.
You're not meant to do sudo -s 'echo test'
, but sudo -s echo test
. In the first case, sudo actually passes echo test
as shell code. In Bourne-like shells, that tries to run a command called 'echo test'
. With a rc
shell, that would run a command called echo
with test
as argument.
Beside SPC, there are a few characters that sudo
escapes. That includes backtick, ;
, |
, (
, )
, *
, &
, =
, but strangely enough, not
$
(or maybe that's the only raison d'être of those -s
/--login
options: to have a shell expand variables).
It does include @
though. If you look at the code, it escapes all bytes except the ASCII alnums, _
, -
and $
.
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
So:
sudo -s echo '$@'
sudo
actually runs "$SHELL" -c 'echo $@'
and that explains why in your example, $@
is not expanded by the shell started by sudo --login
but by the one you tell it to run.
In your
sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
sudo
runs root
's login shell (bash
in your case) and tells it to interpret:
sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
The arguments have been joined with spaces and all the SPC, "
, =
, &
, @
characters, but not $
have been escaped. Because those $
have not been escaped that bash login shell will expand $0,
$1, $2
but not $@
because of that backslash.
You can see it with:
$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
So that login bash
shell ends up running sh
with -c
as first argument,
echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"
as second argument and sh
, 1
, 2
as 3rd, 4th and 5th.
To work around it, you can use $0
instead of $0
, because sudo
transforms it to $0
which prevents the login shell from expanding it to the content of its $0
:
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
Edit: history brings light to the reason behind it
more findings while looking at the code. The non-escaping of $
was apparently introduced in 2013 by this change. Which refers to bug#564 which refers to bug#413.
It looks like before that bug#413 was "fixed", sudo
behaved as I expected.
That is:
sudo -i 'some shell code'
Had the login shell interpret that shell code (and sudo -s 'some shell code'
had $SHELL
interpret that shell code). But the bug#413 resolution broke it, because the person who reported the bug didn't understand it worked that way. And the bug#564 broke it further (in an attempt to revert only part of the breakage introduced by the bug#413 resolution).
I went as far as compiling sudo
1.7.1 from 2009 and the -s
/-i
options were working as I'd expect then. 1.7.3
(with bug#413 resolution) worked kind of like you apparently expected:
$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
That escaping introduced in the fix for that bug#413 is easily fooled because banging a in front of all bytes (not character) doesn't work for all.
Beside the obvious case of rc
where is not even a quoting operator, in most shells, newline can't be quoted with
:
$ SHELL=sh sudo -s echo $'anb'
ab
$ SHELL=csh sudo -s echo $'anb'
a b
It also means empty arguments are discarded:
$ sudo printf '<%s>n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>n' a '' b
<a>
<b>
Also, by inserting a before every byte, it's transforming characters into others in charsets where byte 0x5c (the encoding of
) is found in other characters:
$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'xa3``uname`xa3`'
bash: line 2: Linuxα: command not found
α
0xa3 0x60 is Greek Epsilon in that locale. And sudo changes the 0x60s to 0x5c 0x60 and 0xa3 0x5c is Greek Alpha, which explains why the uname
command was run. sudo
changed the
ε`uname`ε
to
α``uname`α`
And of course the behaviour ends up being surprising in that the $-
, $$
, positional parameters and $varname
(where varname
is a valid POSIX variable name) are expanded but not the other parameters (like $@
here, but also $!
, $?
, $*
, $#
) or $varname
or $var[1]
(csh
/tcsh
/zsh
) or $var(1)
(rc
/es
/...).
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee thatsudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".
– sourcejedi
Aug 16 at 20:30
1
@Kusalananda, I never understood why sudo handled-s
/--login
that way. To me, it makes it pointless.
– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
add a comment |
For the same reason
sudo --login echo '$HOME'
Doesn't output $HOME
, but the content of the $HOME
variable (of the target user, as set by sudo
).
In your case, it's not because sh -c doesn't expand the positional parameters, but because the $0
, $1
, $2
were already expanded (by the login shell) by the time sh
was started.
What is more surprising here is why you see all=1 2
. And it's the same reason why you see $@
upon sudo --login echo '$@'
.
Both -s
/--shell
and -i
/--login
run a shell to run the command. But sudo
does it in a very strange way.
I'd expect sudo -s 'some shell code'
to run a shell to interpret some shell code
, but that's not what it does. With -s
, it still kind-of expects to run a single simple command, and tries to quote (with ) some characters in the hope of making it happen through that shell.
You're not meant to do sudo -s 'echo test'
, but sudo -s echo test
. In the first case, sudo actually passes echo test
as shell code. In Bourne-like shells, that tries to run a command called 'echo test'
. With a rc
shell, that would run a command called echo
with test
as argument.
Beside SPC, there are a few characters that sudo
escapes. That includes backtick, ;
, |
, (
, )
, *
, &
, =
, but strangely enough, not
$
(or maybe that's the only raison d'être of those -s
/--login
options: to have a shell expand variables).
It does include @
though. If you look at the code, it escapes all bytes except the ASCII alnums, _
, -
and $
.
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
So:
sudo -s echo '$@'
sudo
actually runs "$SHELL" -c 'echo $@'
and that explains why in your example, $@
is not expanded by the shell started by sudo --login
but by the one you tell it to run.
In your
sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
sudo
runs root
's login shell (bash
in your case) and tells it to interpret:
sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
The arguments have been joined with spaces and all the SPC, "
, =
, &
, @
characters, but not $
have been escaped. Because those $
have not been escaped that bash login shell will expand $0,
$1, $2
but not $@
because of that backslash.
You can see it with:
$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
So that login bash
shell ends up running sh
with -c
as first argument,
echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"
as second argument and sh
, 1
, 2
as 3rd, 4th and 5th.
To work around it, you can use $0
instead of $0
, because sudo
transforms it to $0
which prevents the login shell from expanding it to the content of its $0
:
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
Edit: history brings light to the reason behind it
more findings while looking at the code. The non-escaping of $
was apparently introduced in 2013 by this change. Which refers to bug#564 which refers to bug#413.
It looks like before that bug#413 was "fixed", sudo
behaved as I expected.
That is:
sudo -i 'some shell code'
Had the login shell interpret that shell code (and sudo -s 'some shell code'
had $SHELL
interpret that shell code). But the bug#413 resolution broke it, because the person who reported the bug didn't understand it worked that way. And the bug#564 broke it further (in an attempt to revert only part of the breakage introduced by the bug#413 resolution).
I went as far as compiling sudo
1.7.1 from 2009 and the -s
/-i
options were working as I'd expect then. 1.7.3
(with bug#413 resolution) worked kind of like you apparently expected:
$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
That escaping introduced in the fix for that bug#413 is easily fooled because banging a in front of all bytes (not character) doesn't work for all.
Beside the obvious case of rc
where is not even a quoting operator, in most shells, newline can't be quoted with
:
$ SHELL=sh sudo -s echo $'anb'
ab
$ SHELL=csh sudo -s echo $'anb'
a b
It also means empty arguments are discarded:
$ sudo printf '<%s>n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>n' a '' b
<a>
<b>
Also, by inserting a before every byte, it's transforming characters into others in charsets where byte 0x5c (the encoding of
) is found in other characters:
$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'xa3``uname`xa3`'
bash: line 2: Linuxα: command not found
α
0xa3 0x60 is Greek Epsilon in that locale. And sudo changes the 0x60s to 0x5c 0x60 and 0xa3 0x5c is Greek Alpha, which explains why the uname
command was run. sudo
changed the
ε`uname`ε
to
α``uname`α`
And of course the behaviour ends up being surprising in that the $-
, $$
, positional parameters and $varname
(where varname
is a valid POSIX variable name) are expanded but not the other parameters (like $@
here, but also $!
, $?
, $*
, $#
) or $varname
or $var[1]
(csh
/tcsh
/zsh
) or $var(1)
(rc
/es
/...).
For the same reason
sudo --login echo '$HOME'
Doesn't output $HOME
, but the content of the $HOME
variable (of the target user, as set by sudo
).
In your case, it's not because sh -c doesn't expand the positional parameters, but because the $0
, $1
, $2
were already expanded (by the login shell) by the time sh
was started.
What is more surprising here is why you see all=1 2
. And it's the same reason why you see $@
upon sudo --login echo '$@'
.
Both -s
/--shell
and -i
/--login
run a shell to run the command. But sudo
does it in a very strange way.
I'd expect sudo -s 'some shell code'
to run a shell to interpret some shell code
, but that's not what it does. With -s
, it still kind-of expects to run a single simple command, and tries to quote (with ) some characters in the hope of making it happen through that shell.
You're not meant to do sudo -s 'echo test'
, but sudo -s echo test
. In the first case, sudo actually passes echo test
as shell code. In Bourne-like shells, that tries to run a command called 'echo test'
. With a rc
shell, that would run a command called echo
with test
as argument.
Beside SPC, there are a few characters that sudo
escapes. That includes backtick, ;
, |
, (
, )
, *
, &
, =
, but strangely enough, not
$
(or maybe that's the only raison d'être of those -s
/--login
options: to have a shell expand variables).
It does include @
though. If you look at the code, it escapes all bytes except the ASCII alnums, _
, -
and $
.
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
So:
sudo -s echo '$@'
sudo
actually runs "$SHELL" -c 'echo $@'
and that explains why in your example, $@
is not expanded by the shell started by sudo --login
but by the one you tell it to run.
In your
sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
sudo
runs root
's login shell (bash
in your case) and tells it to interpret:
sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
The arguments have been joined with spaces and all the SPC, "
, =
, &
, @
characters, but not $
have been escaped. Because those $
have not been escaped that bash login shell will expand $0,
$1, $2
but not $@
because of that backslash.
You can see it with:
$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@" sh 1 2
So that login bash
shell ends up running sh
with -c
as first argument,
echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"
as second argument and sh
, 1
, 2
as 3rd, 4th and 5th.
To work around it, you can use $0
instead of $0
, because sudo
transforms it to $0
which prevents the login shell from expanding it to the content of its $0
:
$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
Edit: history brings light to the reason behind it
more findings while looking at the code. The non-escaping of $
was apparently introduced in 2013 by this change. Which refers to bug#564 which refers to bug#413.
It looks like before that bug#413 was "fixed", sudo
behaved as I expected.
That is:
sudo -i 'some shell code'
Had the login shell interpret that shell code (and sudo -s 'some shell code'
had $SHELL
interpret that shell code). But the bug#413 resolution broke it, because the person who reported the bug didn't understand it worked that way. And the bug#564 broke it further (in an attempt to revert only part of the breakage introduced by the bug#413 resolution).
I went as far as compiling sudo
1.7.1 from 2009 and the -s
/-i
options were working as I'd expect then. 1.7.3
(with bug#413 resolution) worked kind of like you apparently expected:
$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2
That escaping introduced in the fix for that bug#413 is easily fooled because banging a in front of all bytes (not character) doesn't work for all.
Beside the obvious case of rc
where is not even a quoting operator, in most shells, newline can't be quoted with
:
$ SHELL=sh sudo -s echo $'anb'
ab
$ SHELL=csh sudo -s echo $'anb'
a b
It also means empty arguments are discarded:
$ sudo printf '<%s>n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>n' a '' b
<a>
<b>
Also, by inserting a before every byte, it's transforming characters into others in charsets where byte 0x5c (the encoding of
) is found in other characters:
$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'xa3``uname`xa3`'
bash: line 2: Linuxα: command not found
α
0xa3 0x60 is Greek Epsilon in that locale. And sudo changes the 0x60s to 0x5c 0x60 and 0xa3 0x5c is Greek Alpha, which explains why the uname
command was run. sudo
changed the
ε`uname`ε
to
α``uname`α`
And of course the behaviour ends up being surprising in that the $-
, $$
, positional parameters and $varname
(where varname
is a valid POSIX variable name) are expanded but not the other parameters (like $@
here, but also $!
, $?
, $*
, $#
) or $varname
or $var[1]
(csh
/tcsh
/zsh
) or $var(1)
(rc
/es
/...).
edited Aug 17 at 6:46
answered Aug 16 at 20:22
Stéphane ChazelasStéphane Chazelas
332k58 gold badges648 silver badges1018 bronze badges
332k58 gold badges648 silver badges1018 bronze badges
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee thatsudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".
– sourcejedi
Aug 16 at 20:30
1
@Kusalananda, I never understood why sudo handled-s
/--login
that way. To me, it makes it pointless.
– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
add a comment |
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee thatsudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".
– sourcejedi
Aug 16 at 20:30
1
@Kusalananda, I never understood why sudo handled-s
/--login
that way. To me, it makes it pointless.
– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Ow, that's horrible. What do they propose to "fix" with quoting the arguments?
– Kusalananda♦
Aug 16 at 20:29
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee that
sudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".– sourcejedi
Aug 16 at 20:30
Answer accepted, in the spirit of "sudo does it in a very strange way". I do not guarantee that
sudo
on my system does everything written (or implied?) in this answer, as per my comment "I was rather hoping to avoid quoting messes :-)".– sourcejedi
Aug 16 at 20:30
1
1
@Kusalananda, I never understood why sudo handled
-s
/--login
that way. To me, it makes it pointless.– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, I never understood why sudo handled
-s
/--login
that way. To me, it makes it pointless.– Stéphane Chazelas
Aug 16 at 20:31
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
@Kusalananda, see edit with the historical context that explains the current mess.
– Stéphane Chazelas
Aug 16 at 21:23
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f535925%2fsh-c-does-not-expand-positional-parameters-if-i-run-it-from-sudo-login%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
sudo -i
is essentially running it with the user's shell. It's as if the whole thing was wrapped inbash -c
. Wrap your own command inbash -c
and maybe some mess of quoting might work.– muru
Aug 16 at 18:58
@muru I see, yes. Thanks. I was rather hoping to avoid quoting messes :-).
– sourcejedi
Aug 16 at 19:01
@muru I used
strace -f -e trace=execve
, and found you were right that sudo is wrapping everything into a single string, to callbash --login -c '...'
. Although, that doesn't tell us exactly why the values can be seen in"$@"
, but not$1
or$2
. (And then I checked further; they are also not in$3
nor$4
nor$5
nor$6
).– sourcejedi
Aug 16 at 19:16
if you want to avoid a mess of quoting (and escaping), just write a shell script and run it with sudo. It'll be a lot easier to write and to debug than trying to do it on the command-line, even if it's just a one-use throwaway script.
– cas
Aug 17 at 2:30