Развернуть фигурные скобки и подстановочные знаки в строковой переменной bash (без eval)

#bash #eval #code-injection #curly-braces #variable-expansion

#bash #eval #внедрение кода #фигурные скобки #переменная-расширение

Вопрос:

Учитывая строковую переменную (не строку) в bash, как можно развернуть ее фигурные скобки (и добавить любые подстановочные знаки файла, такие как звездочки), а затем сохранить результирующее расширение в массиве?

Я знаю, что это возможно с eval :

 #!/usr/bin/env bash

string="{a,b}{1,2}"
array=( $( eval echo $string ) )
echo "${array[@]}"
#a1 a2 b1 b2
  

Но eval это может быть опасно, если мы не доверяем содержимому string (скажем, если это аргумент скрипта). A string со встроенными точками с запятой, кавычками, пробелами, произвольными командами и т. Д. Может Открыть скрипт для атаки.

Есть ли более безопасный способ сделать это расширение в bash? Я подозреваю, что все следующие одинаково небезопасные способы расширения строки:

 #!/usr/bin/env bash

eval echo $string

bash <<< "echo $string"

echo echo $string | bash

bash <(echo echo $string)
  

Возможно, есть команда, отличная от bash, к которой мы можем передать строку, которая выполнит расширение?

В стороне: обратите внимание, что в csh / tcsh это легко, поскольку расширение фигурных скобок происходит после замены переменной (в отличие от bash):

 #!/usr/bin/env csh

set string = "{a,b}{1,2}"
set array = ( $string )
echo "$array"
#a1 a2 b1 b2
  

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

1. Назначения массива расширяют расширения фигурных скобок, не eval требуется! Попробуйте arr=({1,2}); declare -p arr

2. О, подождите, вы начинаете со строковой переменной. Это не сработает, потому что расширение фигурных скобок происходит до расширения параметра.

3. Откуда вы string беретесь? Возможно, есть способ выполнить расширение фигурных скобок на отдельном шаге.

4. Действительно, я начинаю со строковой переменной, а не строки, и хочу развернуть ее в массив. В моем приложении строковая переменная обычно длинная и сложная с фигурными скобками, подстановочными знаками и т. Д. И расширяется в очень длинный массив файлов. Строка также используется в ее нерасширенной форме в другом месте скрипта, например, передается другим командам оболочки, Которые позже будут выполнять расширение после добавления путей и т. Д., Поэтому рефакторинг моего кода, чтобы избежать этого, будет сложным.

5. Звучит так, как будто передача уже расширенного списка вместо чего-то подобного 'a{b,c}*' была бы предпочтительнее. Конечно, это усложняет другие задачи, но это не хакерский подход. Кроме этого, мне бы очень хотелось увидеть ответ на вопрос вместо комментариев, подобных моим (пытаясь убедить вас не делать то, что вы хотели 🙂

Ответ №1:

braceexpand Модуль Python в сочетании с модулем стандартной библиотеки glob можно использовать для достижения того, что вы ищете. Завернутый в оболочку для использования из bash, это может выглядеть так:

 #!/usr/bin/env bash
case $BASH_VERSION in
  ''|[123].*|4.[012].*) echo "ERROR: bash 4.3  required" >amp;2; exit 1;;
esac

# store Python code in a string
expand_py=$(cat <<'EOF'
try:
  import sys, glob, braceexpand
except ImportError as e:
  sys.stderr.write("Did you remember to install the braceexpand Python module?n")
  raise e

for arg in sys.argv[1:]:
  for globexp in braceexpand.braceexpand(arg):
    glob_results = glob.glob(globexp)
    if len(glob_results) == 0:
      sys.stdout.write(globexp)
      sys.stdout.write('')
      continue
    else:
      sys.stdout.write(''.join(['%s' % result for result in glob_results]))
EOF
)

# shell wrapper for the above Python program
expand() {
  local item
  local -n dest_array=$1; shift

  dest_array=( )
  while IFS= read -r -d '' item; do
    dest_array =( "$item" )
  done < <(python3 -c "$expand_py" "$@")
  unset -n dest_array
}

# actually demonstrate usage
expand yourArray '{a,b}{1,2}'
declare -p yourArray
  

…выводит при запуске заданное значение массива:

 declare -a yourArray=([0]="a1" [1]="a2" [2]="b1" [3]="b2")