Переменные параметра bash в задаче скрипта - PullRequest
5 голосов
/ 27 апреля 2011

У меня есть скрипт, который я написал для переключения на root или запуска команды от имени root без пароля. Я отредактировал мой файл / etc / sudoers, чтобы у моего пользователя [matt] было разрешение на запуск / bin / su без пароля. Это содержимое моего скрипта "s":

matt: ~ $ cat ~/bin/s
#!/bin/bash

[ "$1" != "" ] && c='-c'

sudo su $c "$*"

Если нет параметров [просто s], он в основном вызывает sudo su, который идет в root без пароля. Но если я добавлю параметры, переменная $ c будет равна -c, что заставит su выполнить одну команду.

Работает хорошо, за исключением случаев, когда мне нужно использовать пробелы. Например:

matt: ~ $ touch file\ with\ spaces
matt: ~ $ s chown matt file\ with\ spaces 
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file with spaces'
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file\ with\ spaces'
matt: ~ $ 

Как я могу это исправить?

Кроме того, в чем разница между $ * и $ @ ?

Ответы [ 2 ]

8 голосов
/ 28 апреля 2011

Ах, весело с цитированием.Обычно подход, предложенный @John, сработает для этого: используйте "$@", и он не будет пытаться интерпретировать (и запутаться) пробелы и другие забавные символы в ваших параметрах.В этом случае, однако, это не сработает, потому что опция su -c ожидает, что вся команда будет передана как один параметр, а затем запустит новую оболочку, которая анализирует команду (включая запутанность из-за пробелов и тому подобное).,Чтобы избежать этого, вам действительно необходимо повторно заключить в кавычки параметры в строке, которую вы собираетесь передать su -c.Встроенный в bash printf может сделать это:

#!/bin/bash

if [ $# -gt 0 ]; then
    sudo su -c "$(printf "%q " "$@")"
else
    sudo su
fi

Позвольте мне рассказать о том, что здесь происходит:

  1. Вы запускаете команду типа s chown matt file\ with\ spaces
  2. bashразбирает это на список слов: "s" "chown" "matt" "файл с пробелами".Обратите внимание, что в этот момент введенные вами экранированные символы были удалены (хотя они и дали ожидаемый эффект: заставить bash рассматривать эти пробелы как часть параметра, а не как разделители между параметрами).
  3. Когда bash анализирует printf "%q " "$@" команда в скрипте, она заменяет "$@" аргументами скрипта, с разрывами параметров без изменений.Это эквивалентно printf "%q " "chown" "matt" "file with spaces".
  4. printf интерпретирует строку формата "% q" как "печатать каждый оставшийся параметр в форме в кавычках с пробелом после него".Он печатает: "chown matt file \ with \ Spaces", по существу, восстанавливая исходную командную строку (в конце у нее есть дополнительный пробел, но это не проблема).
  5. Это затем передаетсяsudo в качестве параметра (поскольку в конструкции $() есть двойные кавычки, он будет рассматриваться как один параметр для sudo).Это эквивалентно выполнению sudo su -c "chown matt file\ with\ spaces ".
  6. sudo, запускает su и передает остальную часть полученного списка параметров, включая полностью экранированную команду.
  7. su выполняетсяоболочка, и она также передает оставшуюся часть списка параметров.
  8. Оболочка выполняет команду, полученную в качестве аргумента для -c: chown matt file\ with\ spaces.При обычном анализе он будет интерпретировать неэкранированные пробелы как разделители между параметрами, а экранированные пробелы - как часть параметра и игнорировать дополнительное пространство в конце.
  9. Оболочкаработает chown, с параметрами "матовый" и "файл с пробелами".Это делает то, что вы ожидали.

Разве bash не анализирует крик?

3 голосов
/ 28 апреля 2011

"$*" собирает все позиционные параметры ($1, $2, ...) в одно слово, разделенное одним пробелом (в более общем случае, первый символ $IFS).Обратите внимание, что в терминологии оболочки слово может содержать любой символ, включая пробелы: "foo bar" или foo\ bar парсит до одного слова.Например, если есть три аргумента, "$*" эквивалентно "$1 $2 $3".Если аргумента нет, то "$*" эквивалентно "" (пустое слово).

"$@" - это специальный синтаксис, который расширяется до списка позиционных параметров, каждый из которых имеет собственное слово.Например, если есть три аргумента, то "$@" эквивалентно "$1" "$2" "$3".Если аргумента нет, то "$@" эквивалентно ничему (пустой список, а не список с одним пустым словом).

"$@" почти всегда то, что выхотите использовать, в отличие от "$*" или без кавычек $* или $@ (последние два в точности эквивалентны и выполняют генерацию имени файла (также называемое «подстановкой») и разбиение слов по всем позиционным параметрам).

Есть еще одна проблема, которая заключается в том, что su за исключением одной команды оболочки в качестве аргумента -c, и вы передаете ей несколько слов.У вас было подробное объяснение правильного цитирования , но позвольте мне добавить совет о том, как сделать это правильно, что обходит проблемы двойного цитирования.Вы также можете обратиться к https://unix.stackexchange.com/questions/3063/how-do-i-run-a-command-as-the-system-administrator-root для получения дополнительной информации о sudo и su.

sudo уже запускает команду от имени пользователя root, поэтому нет необходимости вызывать su,Если у вашего скрипта нет аргументов, вы можете просто запустить оболочку напрямую;если ваша версия sudo не очень старая, есть вариант для этого: sudo -s.Таким образом, ваш сценарий может быть:

#!/bin/sh
if [ $# -eq 0 ]; then set -- -s; else set -- -- "$@"; fi
exec sudo "$@"

(остальная часть предназначена для обработки редкого случая команды, начинающейся с -.)

Я бы не стал беспокоиться о такойхотя короткий сценарий.Выполнение команды от имени root необычно и достаточно рискованно, поэтому ввод трех дополнительных символов не должен быть проблемой.Запуск оболочки от имени root еще более необычен и рискован и, безусловно, заслуживает еще шести символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...