2010-05-24 2 views
10

Есть более элегантный способ сделать ленивую оценку, чем следующие:Ленивый Оценка в Bash

 
pattern='$x and $y' 
x=1 
y=2 
eval "echo $pattern" 

результаты:

 
1 and 2 

Это работает, но eval "echo ..." просто чувствует себя неаккуратно и может быть небезопасно в некотором роде , Есть ли лучший способ сделать это в Bash?

+0

Мне интересно, почему вы хотите сделать это или то, что вы на самом деле пытаетесь достичь. Иногда 'eval' является правильным или единственным способом, но есть и специальные функции' declare' и 'printf', которые могут быть полезны. И могут быть другие способы добиться того, что вам нужно. –

+0

У меня есть сценарий bash, который я хочу настроить. Я хочу, чтобы у пользователя была возможность указать «шаблон». Позже некоторые переменные в шаблоне будут заменены действиями, выполняемыми скриптом (запросы SQL, вызовы SOAP и другие внутренние утилиты) и передаются в другую программу командной строки. Я несколько новичок в Bash, и что-то об этом подходе просто кажется неправильным. Спасибо, что спросили подробности. – User1

ответ

4

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

$ pattern='$x and $y' 
$ x=1 y=2 envsubst <<< $pattern 
1 and 2 
+0

Я думаю, что материал безопасности немного параноик для этого случая. Мне нравятся дополнительные кавычки. – User1

0

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

pattern='The $a is $b when the $z is $x $c $g.' # simulated input from user (use "read") 
unset results 
for word in $pattern 
do 
    case $word in 
     \$a) 
      results+=($(some_command)) # add output of some_command to array (output is "werewolf" 
      ;; 
     \$b) 
      results+=($(echo "active")) 
      ;; 
     \$c) 
      results+=($(echo "and")) 
      ;; 
     \$g) 
      results+=($(echo "the sky is clear")) 
      ;; 
     \$x) 
      results+=($(echo "full")) 
      ;; 
     \$z) 
      results+=($(echo "moon")) 
      ;; 
      *) 
      do_something # count the non-vars, do a no-op, twiddle thumbs 
      # perhaps even sanitize %placeholders, terminal control characters, other unwanted stuff that the user might try to slip in 
      ;; 
    esac 
done 
pattern=${pattern//\$[abcgxz]/%s} # replace the vars with printf string placeholders 
printf "$pattern\n" "${results[@]}" # output the values of the vars using the pattern 
printf -v sentence "$pattern\n" "${results[@]}" # put it into a variable called "sentence" instead of actually printing it 

Выход будет «Оборотень активна, когда луна полна и ясное небо.» Сама же программа, если шаблон равен '$ x $ z вне $ c $ g, поэтому $ a должен быть $ b.' то выход будет «Полная луна исчезнет, ​​и небо станет ясным, поэтому оборотень должен быть активным».

+1

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

+0

Мой ответ - это способ изолировать сценарий от ненадежного ввода пользователя. Он основан на комментариях OP, связанных с вопросом в ответ на мой запрос. –

2

Один сейф возможность состоит в том, чтобы использовать функцию:

expand_pattern() { 
    pattern="$x and $y" 
} 

Это все. Затем используйте следующим образом:

x=1 y=1 
expand_pattern 
echo "$pattern" 

Вы можете даже использовать x и y как переменные окружения (так что они не установлены в основной объем):

x=1 y=1 expand_pattern 
echo "$pattern" 
+1

Это ответ, который я собирался написать, если он еще не был здесь. (Опять же, я только заметил этот вопрос вообще благодаря добавленному ответу, отскакивая его обратно на первую страницу). :) –