2015-09-28 5 views
1

Я пишу скрипт, который функционирует как «говорящий терминал» (вы вводите команды, и он говорит о выходе), и до сих пор мой код:Выполнение команды, которая хранится в переменной (включая каналы и перенаправления)

#!/bin/bash 
while [ 1=1 ]; do 
echo -n "~>" 
read COMMAND 
espeak "$($COMMAND)" 
done 

и это работает для простых команд:

bash$ ./talkingterminal.sh 
~> ls 
# espeak says "talkingterminal.sh" 

но когда я использую трубы и т.д.:

bash$ ./talkingterminal.sh 
~>ip addr | grep inet 
Command "|" is unknown, try "ip addr help". 
~> 

и эта команда работает в bash и дает ожидаемый результат. любая помощь? спасибо, Martin

+0

Не ответ, но для объяснения, я бы сказал, что это потому, что разделение команд происходит в начальная фаза разбиения слов, поэтому расширение команды не может сделать одну команду в конвейере из двух. Ссылка: [Как выполнить команду, хранящуюся в переменной?] (Http://stackoverflow.com/a/4668800/12274) Следовательно, 'eval'. –

ответ

-1

Простое решение заключается в использовании eval:

#!/bin/bash 
while [ 1=1 ]; do 
echo -n "~>" 
read COMMAND 
espeak "$(eval $COMMAND)" 
done 

Затем вы можете распечатать вывод оболочки, а также легко:

#!/bin/bash 
while [ 1=1 ]; do 
echo -n "~>" 
read COMMAND 
OUTPUT="$(eval $COMMAND)" 
echo $OUTPUT 
espeak "$($OUTPUT)" 
done 
0

Это редкий случай, когда eval Уместно; вы просто помещаете тонкую оболочку вокруг того, что уже имеет неограниченный доступ к командной строке. Что еще более важно, вы не изменяете COMMAND неожиданными способами. То, что пользователь вводит, будет выполняться.

#!/bin/bash 
while : ; do 
    IFS= read -rp "~> " COMMAND 
    espeak "$(eval "$COMMAND")" 
done 

Несколько замечаний:

  1. readbash в встроенных могут отображать пользовательский запрос с использованием опции -p.
  2. Используйте команды : или true, которые всегда имеют успех, для установки бесконечного цикла.
  3. Используйте -r опцию и пустое значение для IFS, чтобы гарантировать, что COMMAND устанавливается в точной линии, набранный пользователем.
  4. Цитировать $COMMAND, так что вся строка передается eval без какого-либо предыдущего разбора синтаксиса.
  5. Обратите внимание, что это все еще не может обрабатывать многострочный ввод таким же образом, как и сама оболочка. (Например, линия, например, for x in 1 2 3; do, не будет запрашивать тело корпуса.)
+0

Downvoters: это то, на что 'eval' на самом деле * предназначен * для.OP уже хочет выполнить произвольный код, вместо того, чтобы предполагать, что 'eval' выполнит только предназначенный подмножество кода, который он может выполнить. – chepner

-1

Вы пытались использовать параметр «-r»? Этот параметр позволяет избежать интерпретации специальных символов. Кроме того, вы должны удалить внешние $() в строке:

espeak "$($COMMAND)" 

Оно должно быть:

espeak "$(COMMAND)" 
+0

'read' не проблема; проблема заключается в том, что расширение '$ COMMAND' подобно этому передает' | 'как аргумент' ipaddr', а не синтаксический анализ всей строки в качестве конвейера. – chepner

+0

'$ (COMMAND)' попытается запустить буквенную команду «COMMAND», а не значение параметра «COMMAND». – chepner