Может ли скрипт оболочки распечатать свой аргумент, заключенный в кавычки, как вы бы написали их в приглашении оболочки?


9

В сценарии оболочки, я понимаю, что это "$@"распространяется на аргументы сценария, цитируя их по мере необходимости. Например, это передает аргументы скрипта в gcc:

gcc -fPIC "$@"

При использовании Баш проход к STDIN синтаксиса , <<<хотя, "@$"не работает , как я ожидал бы его.

#!/bin/bash
cat <<< "$@"

Вызов сценария как ./test.sh foo "bar baz"дает

foo bar baz

Я бы ожидал

foo "bar baz"

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

Ответы:


5

Ну, "$@"расширяется до списка позиционных параметров, один аргумент на позиционный параметр.

Когда вы делаете:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmdвызывается с этими тремя аргументами: пустой строкой foo barи blah<newline>blah. Оболочка будет вызывать execve()системный вызов с чем-то вроде:

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

Если вы хотите восстановить командную строку оболочки (то есть код на языке оболочки), которая бы воспроизводила этот же вызов, вы можете сделать что-то вроде:

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

Или с zsh, запрашивая различные типы цитат:

set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

Или zsh, bashили ksh93(здесь для bashYMMV с другими оболочками):

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

Вы также можете использовать опцию оболочки xtrace, которая заставляет оболочку печатать то, что она собирается выполнить:

(PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

Выше мы выполнили команду :no-op с cmdпозиционными параметрами в качестве аргумента. Моя оболочка напечатала их красивым цитируемым способом, подходящим для повторного ввода в оболочку. Не все оболочки делают это.


5
`"$@"` expands to the script arguments, quoting them as needed

Нет, это не то, что происходит. Вызов программы принимает список аргументов, каждый аргумент является строкой. При запуске программы оболочки ./test.sh foo "bar baz", это создает вызов с тремя аргументами: ./test.sh, fooи bar baz. (Нулевой аргумент - это имя программы; это позволяет программам знать, под каким именем они называются.) Цитирование - это функция оболочки, а не функция вызовов программ. Оболочка создает этот список при вызове.

"$@"напрямую копирует список аргументов, передаваемых скрипту или функции, в список аргументов в вызове, где он используется. Здесь нет цитирования, поскольку в этих списках не выполняется синтаксический анализ.

В cat <<< "$@", вы используете "$@"в контексте, где требуется одна строка. <<<Operator` требуется строка, а не список строк. В этом контексте bash берет элементы списка и соединяет их с пробелом между ними.

Для отладки сценария, если вы запускаете set -x( set +xчтобы отключить), это активирует режим трассировки, в котором каждая команда печатается перед выполнением. В bash эта трассировка содержит кавычки, которые позволяют вставить команду обратно в оболочку (это не так для каждой shреализации).

Если у вас есть строка и вы хотите превратить ее в синтаксис исходного кода оболочки, который анализирует исходную строку, вы можете заключить ее в одинарные кавычки и заменить каждую отдельную кавычку внутри строки на '\''.

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

Синтаксис замены строки является специфичным для ksh93 / bash / zsh / mksh. В обычном sh вам нужно перебрать строку.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

2

"$@" расширяет аргументы скрипта, цитируя их по мере необходимости

Ну вроде. Для практических целей это должно быть достаточно близко, и справочное руководство говорит, что"$@" is equivalent to "$1" "$2" ...

Итак, с двумя параметрами fooи bar bazони будут похожи:

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(За исключением того, что если параметры содержат специальные символы вместо простых строк, они не будут расширены снова после расширения $@и $1...)

Но даже если мы рассмотрим $@замену параметров в кавычках, кавычек не будет, echoчтобы увидеть, подобно тому, как gccэто не получает кавычек либо.

<<<это немного исключение из правила "$@"== "$1" "$2" ..., прямо упоминается, что The result is supplied as a single string to the command on its standard inputпосле прохождения расширения параметров и переменных и удаления кавычек среди других. Так что, как обычно, <<< "foo"просто дает в fooкачестве входных данных, так же, как somecmd "foo"дает только fooв качестве аргумента.

Называя сценарий как ./test.sh foo "bar baz"[...] я бы ожидал foo "bar baz"

Если бы кавычки остались, все равно должно было быть "foo" "bar baz". Оболочка или любая запущенная команда не имеет никакого представления о том, что было в кавычках при запуске команды. Или, если даже было какое-либо цитирование, системный вызов просто получает список строк с нулевым символом в конце, а кавычки - только особенность языка оболочки. Другие языки могут иметь другие соглашения.


0

Альтернативное решение для Bash

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bash не поддерживает вложенные замены, поэтому спасибо /programming/12303974/assign-array-to-variable#12304017 за то, как показать, как переназначить массив. См. man bash( Https://linux.die.net/man/1/bash ) подробную информацию о массивах, расширении и замене шаблонов (в разделе раскрытие параметров).

Анализ

Bash помещает параметры командной строки в виде массива в $@

q содержит символ цитирования

Двойные кавычки вокруг раскрытия параметров ${ ... }сохраняют отдельные параметры в виде отдельных элементов, а их обтекание ( )позволяет назначать их в виде массива для переменной.

/#/$qв расширении параметра заменяет начало шаблона (например, регулярное выражение ^) на символ цитирования.

/%/$qв расширении параметра заменяет конец шаблона (например, регулярное выражение $) на символ цитирования.

Вариант использования: запрос MySQL для получения списка адресов электронной почты из командной строки

В приведенные выше инструкции внесены некоторые изменения, чтобы использовать другой символ цитирования, добавить запятые между параметрами и убрать последнюю запятую. И, конечно, я плохо себя чувствую, помещая пароль в вызов mysql. Так что судись со мной.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END

Просто отметьте, что цитирование с двойными кавычками не очень помогает в защите от обратных косых черт, $расширений и других двойных кавычек. Вот почему другие ответы используют одинарные кавычки при переходе на несколько длин для обработки одинарных кавычек внутри строки или используют собственные функции оболочки для создания цитируемой копии строки.
ilkkachu

@ilkkachu должным образом отметил! Вот почему (среди прочих причин) я проголосовал за все предыдущие ответы. Кроме того, почему я добавил этот вариант использования. Надеюсь, что идеальное не враг хорошего.
Джефф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.