Bash: расширение скобок в сценариях не работает из-за нежелательного выхода

Я хочу сделать что-то подобное в скрипте bash. я использую bash 4.1.10,

# rm -rf /some/path/{folder1,folder2,folder3}

Хорошо работает (и, как и ожидалось) от самой оболочки. Удаляет 3 нужные папки, оставляя все остальные нетронутыми.

Когда я помещаю это в сценарий, происходит нечто нежелательное. Например, мой скрипт:

#!/bin/bash
set -x
VAR="folder1,folder2,folder3"
rm -rf /some/path/{$VAR}

Когда я выполняю этот скрипт, папки не удаляются.

Я думаю, что это связано с тем, что происходит нежелательное цитирование. Вывод из скрипта с помощью #!/bin/bash -x:

rm -rf '/some/path/{folder1,folder2,folder3}'

что, конечно, не может быть успешным из-за ' Метки.

Как я могу заставить это работать в моем сценарии?

9 ответов

Решение

Согласно справочной странице:

Порядок расширений: расширение фигурных скобок, расширение тильды, расширение параметров, переменных и арифметики и подстановка команд (выполняется слева направо), разбиение слов и расширение имени пути.

Чтобы обойти это, добавьте еще один уровень расширения:

eval "rm -rf /some/path/{$VAR}"

Поскольку вы пишете сценарий, нет причин писать сложный в обслуживании код с использованием eval трюки

VAR="f1,f2,f3"
IFS=,
set -- $VAR
for f; do
  rm -r "/path/to/$f"
done

или же

VAR=( f1 f2 f3 )
for f in "${VAR[@]}"; do 
  rm -r "/path/to/$f"
done

Если ваш код может быть переставлен, вы можете использовать echo и подстановка команд в bash. Что-то вроде этого:

#!/bin/bash
set -x
VAR=`echo /some/path/{folder1,folder2,folder3}`
rm -rf $VAR

Нет, это связано с тем, что расширение скобок происходит перед расширением параметра. Найдите другой способ сделать это, например, с xargs,

xargs -d , -I {} rm -rf /some/path/{} <<< "$VAR"

Вам необходимо включить флаг braceexpand:

#!/bin/bash
set -B
for i in /some/path/{folder1,folder2,folder3}
do
  rm -rf "$i"
done

Еще один трюк, который вы можете использовать (вместо опасного eval) просто echo внутри подоболочки. Это работает, например:

paths=`echo /some/path/{folder1,folder2,folder3}`
echo rm -rf $paths

выходы:

rm -rf /some/path/folder1 /some/path/folder2 /some/path/folder3

по желанию. (Удалите "эхо" во второй строке, чтобы он действительно rm.)


Важным моментом является то, что bash выполняет раскрытие фигурных скобок перед раскрытием параметров, поэтому вы никогда не захотите помещать разделенный запятыми список (заключенный в фигурные скобки или нет) в переменную - если вы это сделаете, то вам придется прибегнуть к eval, Однако вы можете поместить список разделенных пробелами строк в переменную, выполнив раскрытие фигурной скобки в подоболочке перед присваиванием.

#!/bin/bash
set -x
VAR="folder1,folder2,folder3"
eval "rm -rf /some/path/{$VAR}"

Редактировать Остаток только для информации. Я стараюсь быть информативным, но не многословным:_)

Последние удары имеют опцию globstar. Это может пригодиться в будущем

shopt -s globstar
rm -rfvi some/**/folder?

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

eval "rm -rf /some/path/{$VAR}"

Замещать {$VAR} от ${VAR}:-)

Другие вопросы по тегам