Как сохранить команду в переменной в скрипте оболочки? - PullRequest
87 голосов
/ 11 апреля 2011

Я хотел бы сохранить команду для использования в более поздний период в переменной (не вывод команды, а сама команда)

У меня есть простой скрипт следующим образом:

command="ls";
echo "Command: $command"; #Output is: Command: ls

b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)

Однако, когда я пытаюсь что-то немного более сложное, это терпит неудачу.Например, если я сделаю

command="ls | grep -c '^'";

Вывод:

Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory

Любая идея, как я могу сохранить такую ​​команду (с трубами / несколькими командами) в переменной для последующего использования

Ответы [ 4 ]

125 голосов
/ 11 апреля 2011

Используйте eval:

x="ls | wc"
eval "$x"
y=$(eval "$x")
echo "$y"
31 голосов
/ 18 мая 2017

Do not use eval! Это имеет большой риск введения произвольного выполнения кода.

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

Поместите его в массив и разверните все слова с двойными кавычками "${arr[@]}" до , а не , чтобы IFS разбил слова из-за Разделение слов .

cmdArgs=()
cmdArgs=('date' '+%H:%M:%S')

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

declare -p cmdArgs
declare -a cmdArgs='([0]="date" [1]="+%H:%M:%S")'

и выполните команды как

"${cmdArgs[@]}"
23:15:18

(или) вообще используйте функцию bash для запуска команды,

cmd() {
   date '+%H:%M:%S'
}

и вызывать функцию просто

cmd

POSIX sh не имеет массивов, поэтому самое близкое, что вы можете получить, - это создать список элементов в позиционных параметрах. Вот POSIX sh способ запуска почтовой программы

# POSIX sh
# Usage: sendto subject address [address ...]
sendto() {
    subject=$1
    shift
    first=1
    for addr; do
        if [ "$first" = 1 ]; then set --; first=0; fi
        set -- "$@" --recipient="$addr"
    done
    if [ "$first" = 1 ]; then
        echo "usage: sendto subject address [address ...]"
        return 1
    fi
    MailTool --subject="$subject" "$@"
}

Обратите внимание, что этот подход может обрабатывать только простые команды без перенаправлений. Он не может обрабатывать перенаправления, конвейеры, циклы for / while, операторы if и т. Д.

21 голосов
/ 17 ноября 2014
var=$(echo "asdf")
echo $var
# => asdf

Используя этот метод, команда сразу оценивается и ее возвращаемое значение сохраняется.

stored_date=$(date)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015

То же самое с обратным ключом

stored_date=`date`
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015

Использование eval в $(...)он не будет оцениваться позже

stored_date=$(eval "date")
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015

Используя eval, он оценивается при использовании eval

stored_date="date" # < storing the command itself
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:05 EST 2015
# (wait a few seconds)
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:16 EST 2015
#                     ^^ Time changed

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

stored_date="date -u"
# ...

Для сценариев bash это редко актуально, но последнее замечание.Будьте осторожны с eval.Eval - это только строки, которыми вы управляете, но не строки, полученные от ненадежного пользователя или созданные из ненадежного пользовательского ввода.

  • Спасибо @CharlesDuffy за напоминание процитировать команду!
0 голосов
/ 11 апреля 2011

Нет необходимости хранить команды в переменных, даже если вам потребуется использовать их позже.просто выполните это как обычно.Если вы храните переменную, вам понадобится какой-то оператор eval или вызовите какой-нибудь ненужный процесс оболочки для «выполнения вашей переменной».

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