Сценарий Bash — переменное содержимое в качестве команды для запуска

#bash #dialog

#bash

Вопрос:

У меня есть скрипт на Perl, который выдает мне определенный список случайных чисел, соответствующих строкам файла. Затем я хочу извлечь эти строки из файла, используя sed .

 #!/bin/bash
count=$(cat last_queries.txt | wc -l)
var=$(perl test.pl test2 $count)
 

Переменная var возвращает вывод, подобный: cat last_queries.txt | sed -n '12p;500p;700p' . Проблема в том, что я не могу выполнить эту последнюю команду. Я пробовал $var , но вывод неверен (если я запускаю команду вручную, она работает нормально, так что проблем нет). Каков правильный способ сделать это?

P.S: Конечно, я мог бы выполнить всю работу на Perl, но я пытаюсь научиться этому, потому что это может помочь мне в других ситуациях.

Ответ №1:

Вам просто нужно сделать:

 #!/bin/bash
count=$(cat last_queries.txt | wc -l)
$(perl test.pl test2 $count)
 

Однако, если вы хотите вызвать свою команду Perl позже, и именно поэтому вы хотите присвоить ее переменной, то:

 #!/bin/bash
count=$(cat last_queries.txt | wc -l)
var="perl test.pl test2 $count" # You need double quotes to get your $count value substituted.

...stuff...

eval $var
 

Согласно справке Bash:

 ~$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.
 

Комментарии:

1. Без eval этого не будет работать должным образом. Попробуйте var='printf "%sn" "first item" "second item" "third item" и сравните $var с eval "$var" . Первое просто неверно по причинам, описанным в BashFAQ # 50 .

2. … то есть: $(perl test.pl test2 $count) должно быть eval "$(perl test.pl test2 $count)" , чтобы избежать ошибок, которые документирует BashFAQ # 50.

3. Это работает (у меня также были проблемы с командной строкой, содержащей » | «, как в вопросе). Если необходимо получить вывод (для стандартного вывода) , то это можно сделать с помощью someoutput=$(eval $var) , а не просто eval $var . Возможно, немного расширить ответ?

Ответ №2:

Вы, вероятно, ищете eval $var .

Комментарии:

1. Это сработало при $($cmd)) сбое. Он всегда говорит, что команда не найдена. Спасибо!

Ответ №3:

Существует 2 основных способа выполнения строковой команды в сценарии оболочки, независимо от того, задана она как параметр или нет.

 COMMAND="ls -lah"
$(echo $COMMAND)
 

или

 COMMAND="ls -lah"
bash -c $COMMAND
 

Комментарии:

1. Спасибо, это немного помогло. Но у вас есть идея, почему bash -c "cd /home/user/dev/" это не работает?

2. @andylib93 cd влияет только на текущий процесс, если вы запустите bash -c "cd /home/user/dev/" , он запустит новый bash процесс и запустится cd в рамках этого процесса. Дочерний процесс отлично изменит каталог, но это не повлияет на оболочку, в которой вы запустили bash команду.

Ответ №4:

 line=$((${RANDOM} % $(wc -l < /etc/passwd)))
sed -n "${line}p" /etc/passwd
 

вместо этого просто с вашим файлом.

В этом примере я использовал файл /etc/password, используя специальную переменную ${RANDOM} (о которой я узнал здесь ), и sed выражение, которое у вас было, разница только в том, что я использую двойные кавычки вместо одинарных, чтобы разрешить расширение переменной.

Ответ №5:

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

 function echo_arguments() {
  echo "Argument 1: $1"
  echo "Argument 2: $2"
  echo "Argument 3: $3"
  echo "Argument 4: $4"
}

# Note we are passing 3 arguments to `echo_arguments`, not 4
eval echo_arguments arg1 arg2 "Some arg"
 

Результат:

 Argument 1: arg1
Argument 2: arg2
Argument 3: Some
Argument 4: arg
 

Обратите внимание, что, хотя «Некоторый аргумент» был передан как один аргумент, eval считайте его как два.

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

 # The regular bash eval works by jamming all its arguments into a string then
# evaluating the string. This function treats its arguments as individual
# arguments to be passed to the command being run.
function eval_command() {
  "$@";
}
 

Обратите внимание на разницу между выводом eval и новой eval_command функцией:

 eval_command echo_arguments arg1 arg2 "Some arg"
 

Результат:

 Argument 1: arg1
Argument 2: arg2
Argument 3: Some arg
Argument 4:
 

Комментарии:

1. Чтобы избежать вычисления каждого элемента после пробела, как в вашем первом примере с eval, заключите его в одинарные кавычки : eval 'echo_arguments arg1 arg2 "Some arg"' . Тогда результат будет таким, как во втором примере.

Ответ №6:

Лучшие способы сделать это

Использование функции:

 # define it
myls() {
    ls -l "/tmp/test/my dir"
}

# run it
myls
 

Использование массива:

 # define the array
mycmd=(ls -l "/tmp/test/my dir")

# run the command
"${mycmd[@]}"
 

Ответ №7:

 cmd="ls -atr ${HOME} | tail -1" <br/>
echo "$cmd"  <br/>
VAR_FIRST_FILE=$( eval "${cmd}" )  <br/>
 

или

 cmd=("ls -atr ${HOME} | tail -1")  <br/>
echo "$cmd"  <br/>
VAR_FIRST_FILE=$( eval "${cmd[@]}" )