Сохранение кавычек в параметрах функции bash - PullRequest
21 голосов
/ 16 июля 2010

То, что я хотел бы сделать, это взять в качестве входных данных для функции строку, которая может включать кавычки (одинарные или двойные) и отображать эту строку в точности так, как она была предоставлена ​​функции.Например:

function doit {
   printf "%s " ${@} 
   eval "${@}"
   printf " # [%3d]\n" ${?}
}

Что при следующем вводе

doit VAR=42
doit echo 'single quote $VAR'
doit echo "double quote $VAR"

дает следующее:

VAR=42  # [  0]
echo single quote $VAR  # [  0]
echo double quote 42  # [  0]

Таким образом, семантика раскрытия переменной сохраняется какЯ ожидаю, но я не могу получить точный формат строки, как это было предоставлено функции.То, что я хотел бы, это иметь doit echo 'single quote $VAR' результат в echo 'single quote $VAR'.

Я уверен, что это связано с обработкой bash аргументов перед их передачей в функцию;Я просто ищу способ обойти это (если возможно).

Редактировать

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

В то время как я могу получить желаемое поведение, описанное выше, выполнив что-то вроде

while read line ; do 
   doit ${line}
done < ${INPUT}

Этот подход терпит неудачу перед лицом управляющих структур (то есть if, while, так далее).Я думал об использовании set -x, но у него также есть свои ограничения: " становится ' и состояние выхода не отображается для команд, которые не работают.

Ответы [ 8 ]

10 голосов
/ 22 ноября 2011

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

Я придумал что-то, что не сохраняет командную строку точно так же, какнабрал, но передал аргументы правильно и показал вам, что они были.

Вот мой сценарий, настроенный на тень ls:

CMD=ls
PARAMS=""

for PARAM in "$@"
do
  PARAMS="${PARAMS} \"${PARAM}\""
done

echo Running: ${CMD} ${PARAMS}
bash -c "${CMD} ${PARAMS}"
echo Exit Code: $?

И это пример вывода:

$ ./shadow.sh missing-file "not a file"
Running: ls "missing-file" "not a file"
ls: missing-file: No such file or directory
ls: not a file: No such file or directory
Exit Code: 1

Итак, как вы можете видеть, он добавляет кавычки, которых изначально не было, но сохраняет аргументы с пробелами, в которых я и нуждался.

7 голосов
/ 16 июля 2010

Причина этого в том, что bash интерпретирует аргументы, как вы и думали. Кавычек просто больше нет, когда она вызывает функцию, поэтому это невозможно. Это работает в DOS, потому что программы могут интерпретировать командную строку сами по себе, а не то, что это вам помогает!

4 голосов
/ 12 июня 2014

Хотя @ Peter Westlake 's ответ правильный, и нет кавычек для сохранения, можно попытаться вывести, если кавычки там, где это требуется, и, таким образом, переданы изначально.Лично я использовал эту функцию requote, когда мне нужно было доказать в своих журналах, что команда выполнялась с правильным цитированием:

function requote() {
    local res=""
    for x in "${@}" ; do
        # try to figure out if quoting was required for the $x:
        grep -q "[[:space:]]" <<< "$x" && res="${res} '${x}'" || res="${res} ${x}"
    done
    # remove first space and print:
    sed -e 's/^ //' <<< "${res}"
}

И вот как я ее использую:

CMD=$(requote "${@}")
# ...
echo "${CMD}"
3 голосов
/ 30 июля 2013
doit echo "'single quote $VAR'"
doit echo '"double quote $VAR"'

Оба будут работать.

bash удалит только внешний набор кавычек при входе в функцию.

1 голос
/ 19 марта 2014

Это:

ponerApostrofes1 () 
{
    for (( i=1; i<=$#; i++ ));
    do
        eval VAR="\${$i}"; 
        echo \'"${VAR}"\';
    done; 
    return; 
}

Например, возникают проблемы, когда параметры имеют апострофы.

Эта функция:

ponerApostrofes2 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n \'${PARAM//\'/\'\\\'\'}\'' ';
    done;
    return
}

решает упомянутую проблему, и вы можете использовать параметры, включая апострофы внутри, например, "Porky's", и возвращать, по-видимому (?), Одну и ту же строку параметров при заключении каждого параметра в кавычки; если нет, он цитирует это. Удивительно, но я не понимаю, почему, если вы используете его рекурсивно, он не возвращает тот же список, но каждый параметр снова цитируется. Но если вы сделаете эхо каждого из них, вы восстановите исходный параметр.

Пример:

$ ponerApostrofes2 'aa aaa' 'bbbb b' 'c' 
'aa aaa' 'bbbb b' 'c'

$ ponerApostrofes2 $(ponerApostrofes2 'aa aaa' 'bbbb b' 'c' )
''\''aa' 'aaa'\''' ''\''bbbb' 'b'\''' ''\''c'\''' 

И

$ echo ''\''bbbb' 'b'\'''
'bbbb b'
$ echo ''\''aa' 'aaa'\'''
'aa aaa'
$ echo ''\''c'\''' 
'c'

А вот этот:

ponerApostrofes3 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n ${PARAM//\'/\'\\\'\'} ' ';
    done;
    return
}

возвращает на один уровень цитаты меньше, тоже не работает, ни чередуя оба рекурсивно.

1 голос
/ 16 июля 2010

Bash удалит кавычку, когда вы передадите строку с кавычкой в ​​качестве аргумента командной строки.Цитата просто больше не существует, когда строка передается вашему сценарию.У вас нет возможности узнать, есть ли одинарная или двойная кавычка.

Что вы, вероятно, можете сделать, это вот что:

doit VAR=42
doit echo \'single quote $VAR\'
doit echo \"double quote $VAR\"

В вашем скрипте вы получите

echo 'single quote $VAR'
echo "double quote $VAR"

Или сделайте это

doit VAR=42
doit echo 'single quote $VAR'
doit echo '"double quote $VAR"'

В вашем скрипте вы получите

echo single quote $VAR
echo "double quote $VAR"
0 голосов
/ 02 августа 2017

Если оболочка не поддерживает подстановку шаблона, т. Е. ${param/pattern/string}, то следующее выражение sed можно использовать для безопасной кавычки любой строки, чтобы она снова eval превратилась в один параметр:

sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"

Комбинируя это с printf, можно написать небольшую функцию, которая будет принимать любой список строк, созданных расширением имени файла, или "$@" и превращать его во что-то, что можно безопасно передать в eval, чтобы развернуть его в аргументы для другой команды, сохраняя при этом разделение параметров.

# Usage: quotedlist=$(shell_quote args...)
#
# e.g.:  quotedlist=$(shell_quote *.pdf)    # filenames with spaces
#
# or:    quotedlist=$(shell_quote "$@")
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
#
#   eval "set -- $quotedlist"
#   for str in "$@"; do
#       # fiddle "${str}"
#   done
#
# or like this:
#
#   eval "\$a_command $quotedlist \$another_parameter"
#
shell_quote()
{
    local result=''
    local arg
    for arg in "$@" ; do

        # Append a space to our result, if necessary
        #
        result=${result}${result:+ }

        # Convert each embedded ' to \' , then insert ' at the
        # beginning of the line, and append ' at the end of
        # the line.
        #
        result=${result}$(printf "%s\n" "$arg" | \
            sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")
    done

    # use printf(1) instead of echo to avoid weird "echo"
    # implementations.
    #
    printf "%s\n" "$result"
}

В некоторых ситуациях может быть проще (и, возможно, безопаснее, то есть избегать eval) использовать символ «невозможный» в качестве разделителя полей, а затем использовать IFS для повторного управления расширением значения.

0 голосов
/ 16 июля 2010

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

...