Unix & Linux Asked by alecxs on November 17, 2021
Are nested `commands`
using (deprecated) backticks executed with root permissions (from sudo
command line) while command substitution $(...)
is not?
for example
sudo mount `blkid -u /dev/sda1` ...
sudo mount `sudo blkid -u /dev/sda1` ...
vs
sudo mount $(blkid -u /dev/sda1) ...
sudo mount $(sudo blkid -u /dev/sda1) ...
is there a difference between methods? i am not even quite sure which of the four lines is the right usage (regarding sudo
, not blkid it’s just pseudo code). or is it shell dependent?
`...`
and $(...)
are the same thing with different syntax, one if from the Bourne shell, the other from the Korn shell, the former is deprecated but still supported by Bourne-like shells for backward compatibility with the ancient Bourne shell.
Other shells have different syntax for that. For instance, fish
has (...)
and rc
/es
have `cmd
or `{more complex cmd}
, or `(sep){cmd}
to specify different splitting behaviour. ksh93
and mksh
also have ${ ...; }
(no subshell) variant.
In any case, that's syntax in the shell language, so you need a shell to interpret it and perform the command substitution.
In:
sudo cmd1 `cmd2`
In Bourne-like shells, the shell
cmd2
in a child process with its output redirected to a pipe$IFS
zsh
; some ksh variants also perform brace expansion)sudo
command executed in another child process.sudo
then changes uids and runs the command it is being passed as argument.
If you wanted cmd2
, to be run with the different uid, you'd need sudo
to run a shell to interpret the shell code that performs the command substitution:
sudo sh -c 'cmd1 $(cmd2)'
sudo fish -c 'cmd1 (cmd2)'
sudo rc -c 'cmd1 `cmd2'
and so on.
Note that in Bourne-like shells, command substitution is still performed inside double quotes, so do not do:
sudo sh -c "cmd1 $(cmd2)"
First, that would not run cmd2
as root
, but also the output of cmd2
(this time, not subjected to split+glob as it's within quotes), would be interpreted as sh
code, so would typically constitute a command injection vulnerability. For instance, if cmd2
outputs $(reboot)
, the sh
invoked by sudo
would be asked to interpret cmd1 $(reboot)
and reboot.
Similarly, don't do or sudo sh -c 'cmd1 $(cmd2) '"$var"
if you want to pass the contents of variables of your shell (as opposed to the one started by sudo sh -c 'cmd1 $(cmd2 '"$var"')'
sudo
) to cmd1
or cmd2
. Instead, pass the contents of those variables as extra arguments to sh
(not inside the code argument), or via environment variables (so they become variables of the shell started by sudo
as well):
sudo sh -c 'cmd1 $(cmd2) "$1"' sh "$var"
sudo VAR="$var" sh -c 'cmd1 $(cmd2) "$VAR"'
sudo sh -c 'cmd1 $(cmd2 "$1")' sh "$var"
sudo VAR="$var" sh -c 'cmd1 $(cmd2 "$VAR")'
Here, you can always also do:
sudo cmd1 $(sudo cmd2) "$var"
sudo cmd1 $(sudo cmd2 "$var")
That is have your shell run both commands through two separate invocations of sudo
so both be run with elevated privileges.
As hinted above, in Bourne-like shells (but that also applies, though differently, to csh-like shells), unquoted command substitution is subject to split+glob, so you'd only leave $(cmd2)
unquoted in cmd1 $(cmd2)
if cmd2
is outputting a $IFS
delimited list of wildcard patterns. If you want the output of cmd2
(without the trailing newline characters) to be passed as a whole as one argument to cmd1
, you'd want cmd1 "$(cmd2)"
or more likely cmd1 -- "$(cmd2)"
to make sure that argument is not treated as an option (assuming cmd1
supports that --
end-of-option marker).
So for your particular use case, that would be:
sudo DEVICE="$device" sh -c 'mount -- "$(blkid -u -- "$DEVICE")"'
Or to call mount
only if blkid
succeeds:
sudo DEVICE="$device" sh -c '
output=$(blkid -u -- "$DEVICE") &&
mount -- "$output"
'
Answered by Stéphane Chazelas on November 17, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP