TL;DR:При выполнении команды с параметрами «--shell» или «--login» sudo
экранирует большинство символов (с использованием обратной косой черты ), включая все метасимволы. (кроме $
), также включая пробелы.
Это нарушает каждый случай использования, когда вы хотите использовать оболочку, что делает sudo -s
в основном непригодным для запуска команд оболочки, которые требуют для запуска в качестве команд оболочки.
Вместо sudo -s
используйте sudo sh -c '...'
илиsudo bash -c '...'
(или любое другое ожидаемое значение $SHELL
. )Вместо sudo -i
используйте sudo bash -l -c '...'
вместо (, предполагая, что оболочка root снова является bash.)
Глядя на соответствующую часть исходного кода sudo , есть этот фрагмент:
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
Фрагмент перебирает каждый символ каждого аргумента. Если символ не является буквенно-цифровым, подчеркиванием, дефисом или знаком доллара, он будет экранирован обратной косой чертой.
Это означает, что подстановочные знаки *
, ?
, [...]
и т. д. будут преобразованы в \*
, \?
, \[...\]
.
Также метасимволы, такие как ~
, ;
, |
и т. д., будут преобразованы в \~
, \;
, \|
.
Строка с пробелами ls /root
будет преобразована в ls\ /root
.
По какой-то причине $
является исключением при экранировании, поэтому sudo -s echo '$HOME'
работает, так как $
не будет экранировано. (Обратите внимание, что это работает только для переменных, и даже не для формы ${HOME}
, в этом случае фигурные скобки будут экранированы, что нарушит выражение.)
Последствия этого цитирования заключаются в том, что в большинстве случаев, требующих запуска оболочки от имени пользователя root, формат sudo -s <command>
не дает реальных преимуществ :
|
будет экранирован и тоже не будет работать. for
и if
, обычно должны использовать ;
, которые будут экранированы и также не будут работать. Альтернативой может быть новая строка, но, похоже, нет способа ввести неэкранированную новую строку. Короче говоря, форма sudo -s <command>
работает только в тех случаях, когда не используются символы подстановки, конвейеры, скриптлеты и т. д. Но тогда вам обычно не нужна оболочка для запуска этих команд! В таких случаях обычно достаточно просто запустить sudo <command>
без -s
.
Неясно, какова мотивация реализации sudo. Возможно, чтобы поддерживать некоторую согласованность между обработкой команд при добавлении или удалении -s
. Возможно также потому, что -i
предшествует -s
, и в этом случае есть небольшая разница в том, как устанавливаются переменные окружения...
ВРЕМЕННОЕ РЕШЕНИЕ:Временное решение заключается в явном запуске -c
оболочки.
Для этого нужно знать, что такое $SHELL
для целевого пользователя (, который может не совпадать с текущим пользователем, поэтому прямое использование $SHELL
не всегда правильно.)
Например, вместо sudo -s ls -l '/root/*.cfg'
используйте:
$ sudo bash -c 'ls -l /root/*.cfg'
И вместо sudo -i ls -l '~'
используйте:
$ sudo bash -l -c 'ls -l ~'
(Аргумент -l
bash создает оболочку «login», которая эквивалентна той, что была создана sudo -i
.)