Как сохранить команду в переменной в Linux?


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

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

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 68

4 ответа:

использовать eval:

x="ls | wc"
eval $x
y=$(eval $x)
echo $y

сделать не использовать 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 способ запуска программы mail

# POSIX sh
# Usage: sendto subject address [address ...]
sendto() {
    subject=
    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" "$@"
}
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

то же самое с backtick

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 только строки, которыми вы управляете, никогда не строки, поступающие от ненадежного пользователя или построенные из ненадежного пользовательского ввода.

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