Rm не удаляет файлы по шаблону из script, но работает из командной строки
У меня возникла действительно глупая проблема с оболочкой Linux script. Я хочу удалить все файлы с расширением ".bz2" в каталоге. В script я вызываю
rm "$archivedir/*.bz2"
где $archivedir - путь к каталогу. Должно быть довольно просто, не так ли? Как-то он справляется с этой ошибкой:
rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory
Но есть файл в этом каталоге test.bz2, и если я изменю свой script на
echo rm "$archivedir/*.bz2"
и скопируйте/вставьте вывод этой строки в окно терминала, файл удаляется успешно. Что я делаю неправильно?
Ответы
Ответ 1
TL; DR
Цитата только переменная, а не весь ожидаемый путь с подстановочным знаком
rm "$archivedir"/*.bz2
Объяснение
-
В Unix программы обычно не интерпретируют подстановочные знаки. Оболочка интерпретирует незакодированные подстановочные знаки и заменяет каждый аргумент подстановки списком совпадающих имен файлов.
если $archivedir может содержать пробелы, то rm $archivedir/*.bz2
может не делать то, что вы
-
Вы можете отключить этот процесс, указав символ подстановки, используя двойные или одинарные кавычки или обратную косую черту перед ним. Тем не менее, это не то, что вы хотите здесь, - вы хотите, чтобы подстановочный знак расширялся до списка файлов, которые он соответствует.
-
Будьте осторожны с написанием rm $archivedir/*.bz2
(без кавычек). Разделение слова (т.е. Разбиение командной строки на аргументы) происходит после замены $archivedir. Поэтому, если $archivedir содержит пробелы, тогда вы получите дополнительные аргументы, которые вы не намеревались. Скажем, archivedir /var/archives/monthly/April to June
. Затем вы получите эквивалент записи rm /var/archives/monthly/April to June/*.bz2
, который пытается удалить файлы "/var/archives/month/April", "to" и все файлы, соответствующие "June/*.bz2", который не является что вы хотите.
Правильное решение - написать:
rm "$archivedir"/*.bz2
Ответ 2
Просто, чтобы немного расшириться, bash имеет довольно сложные правила для работы с метасимволами в кавычках. В общем
-
почти ничего не интерпретируется в одинарных кавычках:
echo '$foo/*.c' => $foo/*.c
echo '\\*' => \\*
-
замена оболочки выполняется внутри двойных кавычек, но метасимволы файлов не разворачиваются:
FOO=hello; echo "$foo/*.c" => hello/*.c
-
все внутри backquotes передается на подоболочку, которая их интерпретирует. Переменная оболочки, которая не экспортируется, не определяется в подоболочке. Итак, первая команда эхо-пустая, но второе и третье эхо "bye":
BAR=bye echo `echo $BAR`
BAR=bye; echo `echo $BAR`
export BAR=bye; echo `echo $BAR`
(И получить это, чтобы напечатать, как вы хотите, в SO выполняется несколько попыток, по-видимому, невозможно...)
Ответ 3
Исходная строка
rm "$archivedir/*.bz2"
Можно переписать как
rm "$archivedir"/*.bz2
для достижения такого же эффекта. Расширение подстановки не выполняется должным образом в вашей существующей настройке. Перемещая двойную кавычку к "фронту" пути к файлу (что является законным), вы избегаете этого.
Ответ 4
Кавычки заставляют строку интерпретироваться как строковый литерал, попробуйте удалить их.
Ответ 5
Я видел похожие ошибки при вызове оболочки script как
./shell_script.sh
из другой оболочки script. Это можно исправить, вызвав его как
sh shell_script.sh
Ответ 6
Почему не просто rm -rf */*.bz2
? Работает для меня на OSX.
Ответ 7
Дополнительная информация о внутренней работе: попробуйте ввести:
echo "$archivedir/*.bz2"
в командной строке оболочки. Вы увидите, что он сразу расширяется. Так что rm никогда не видит *; вместо этого это тот список, который передается ему.
Изменить: я вижу, вы в основном пробовали это. Что вы хотите сделать, попробуйте, когда в этом каталоге есть несколько файлов bz2. Затем вы увидите эффект.