Итак, я удалил свою домашнюю папку (или, точнее, все файлы, к которым у меня был доступ для записи). Что случилось то, что у меня было
build="build"
...
rm -rf "${build}/"*
...
<do other things with $build>
в bash-скрипте и, после того, как больше не нужно $build
, удалить объявление и все его использования - кроме rm
. Баш радостно расширяется rm -rf /*
. Да.
Я почувствовал себя глупо, установил резервную копию, переделал потерянную работу. Пытаясь пройти мимо позора.
Теперь я задаюсь вопросом: каковы методы написания сценариев bash, чтобы такие ошибки не возникали или, по крайней мере, менее вероятны? Например, если бы я написал
FileUtils.rm_rf("#{build}/*")
в сценарии на Ruby переводчик жаловался на то, что его build
не объявили, поэтому язык защищает меня.
То, что я рассмотрел в bash, помимо загона rm
(что, как упоминают многие ответы в смежных вопросах, не является проблемой):
rm -rf "./${build}/"*
Это убило бы мою текущую работу (репозиторий Git), но ничего больше.- Вариант / параметризация
rm
этого требует взаимодействия при работе вне текущего каталога. (Не удалось найти.) Подобный эффект.
Это так или есть другие способы написания bash-скриптов, которые в этом смысле «надежны»?
источник
rm -rf "${build}/*
независимо от того, куда идут кавычки.rm -rf "${build}
будет делать то же самое из-заf
.set -u
не так хмурится, какset -e
есть, но у него все еще есть свои ошибки.#! /usr/bin/env ruby
в начало каждого сценария оболочки и забудьте о bash;)Ответы:
или же
Это заставит текущую оболочку рассматривать расширения неустановленных переменных как ошибку:
set -u
иset -o nounset
являются параметрами оболочки POSIX .Пустое значение будет не вызовет ошибку , хотя.
Для этого используйте
Расширение
${variable:?word}
будет расширяться до значения,variable
если оно не пусто или не установлено. Если он пуст или не установлен, приword
стандартной ошибке будет отображаться символ, и оболочка будет рассматривать расширение как ошибку (команда не будет выполнена, и если она выполняется в неинтерактивной оболочке, это завершится). Выход из:
out приведет к ошибке только для неустановленного значения, как показано нижеset -u
.${variable:?word}
является расширение параметр POSIX .Ни один из них не приведет к завершению интерактивной оболочки, если
set -e
(илиset -o errexit
) также не действует.${variable:?word}
заставляет сценарии завершиться, если переменная пуста или не установлена.set -u
приведет к завершению работы сценария, если используется вместе сset -e
.Что касается вашего второго вопроса. Нет способа ограничить
rm
не работать за пределами текущего каталога.В реализации GNU
rm
есть--one-file-system
опция, которая останавливает рекурсивное удаление смонтированных файловых систем, но это настолько близко, насколько я думаю, мы можем получить, не заключаяrm
вызов в функцию, которая фактически проверяет аргументы.Как примечание стороны:
${build}
точно эквивалентно,$build
если только расширение не происходит как часть строки, где непосредственно следующий символ является допустимым символом в имени переменной, например, в"${build}x"
.источник
${build:?msg}
или нет${build?msg}
? 2) В контексте чего-то вроде скриптов сборки, я думаю, что для безопасного удаления было бы неплохо использовать инструмент, отличный от rm: мы знаем, что не хотим работать за пределами текущего каталога, поэтому мы делаем это явным образом, используя ограниченные команда. Вообще нет необходимости делать rm более безопасным.:
(это вызовет ошибку только для неустановленных переменных). Я не смею пытаться написать инструмент, который бы справлялся с удалением файлов исключительно по определенному пути при любых обстоятельствах. Разбор путей и забота / не забота о символических ссылках и т. Д. В настоящее время слишком сложна для меня.Я собираюсь предложить нормальные проверки с использованием
test
/[ ]
Вы были бы в безопасности, если бы написали свой сценарий так:
В
[ -n "${build}" ]
проверяет,"${build}"
является ненулевой длиной строкой .||
Является логическим ИЛИ оператор в ударе. Это вызывает выполнение другой команды, если первая не удалась.Таким образом,
${build}
был пустой / неопределенный / и т. Д. скрипт завершился бы (с кодом возврата 1, что является общей ошибкой).Это также защитило бы вас, если бы вы удалили все варианты использования,
${build}
потому[ -n "" ]
что всегда будут ложными.Преимущество использования
test
/[ ]
состоит в том, что есть много других более значимых проверок, которые он также может использовать.Например:
источник
${variable:?word}
предлагает @Kusalananda?test
(то есть[
) имеет много других релевантных проверок, таких как-d
(аргумент - это каталог),-f
(аргумент - это файл),-O
(файл принадлежит текущему пользователю) и так далее.${build:?word}
вместе с ней?${variable:?word}
Формат не защитит вас , если вы удалите все экземпляры переменных.if
. 2) «Вы бы тоже не удалили $ {build:? Word} вместе с ним» - сценарий таков, что я пропустил использование переменной. Использование${v:?w}
защитило бы меня от повреждений. Если бы я удалил все обычаи, очевидно, даже простой доступ не был бы вредным.В вашем конкретном случае я переработал «удаление» в прошлом, чтобы вместо этого переместить файлы / каталоги (предполагая, что / tmp находится в том же разделе, что и ваш каталог):
За кулисами это перемещает ссылки верхнего уровня на файлы /
$trashdir
каталоги из источника в структуры каталогов назначения на одном и том же разделе, и не тратит время на обход структуры каталогов и освобождение дисковых блоков для каждого файла. Это приводит к гораздо более быстрой очистке, когда система активно используется, в обмен на немного более медленную перезагрузку (/ tmp очищается при перезагрузках).В качестве альтернативы, запись cron для периодической очистки /tmp/.trash-$USER будет препятствовать заполнению / tmp для процессов (например, сборок), которые занимают много дискового пространства. Если ваш каталог находится в другом разделе как / tmp, вы можете создать аналогичный / tmp-подобный каталог в вашем разделе и использовать вместо этого cron.
Но самое главное, что если вы облажаете переменные каким-либо образом, вы можете восстановить содержимое до того, как произойдет очистка.
источник
/tmp
находится на другом разделе (я думаю, что это всегда для меня, по крайней мере, для сценариев, которые выполняются в пространстве пользователя); затем необходимо изменить папку для мусора (и она не получит выгоды от обработки ОС/tmp
).mktemp -d
чтобы получить этот временный каталог, не наступая на пальцы другого процесса (и чтобы правильно соблюдать$TMPDIR
).Используйте замену параметра bash, чтобы назначить значения по умолчанию, когда переменная неинициализирована, например:
rm -rf $ {переменная: - "/ несуществующая"}
источник
Я всегда стараюсь начинать свои скрипты Bash со строки
#!/bin/bash -ue
.-e
означает «провал на первом uncathed е rror»;-u
означает «провал на первом использовании из U ndeclared переменной».Более подробную информацию можно найти в большой статье. Используйте неофициальный строгий режим Bash (если вы не любите отладку) . Также автор рекомендует использовать,
set -o pipefail; IFS=$'\n\t'
но для моих целей это излишне.источник
Общий совет, чтобы проверить, установлена ли ваша переменная, является полезным инструментом для предотвращения такого рода проблем. Но в этом случае было более простое решение.
Скорее всего, нет необходимости перемещать содержимое
$build
каталога, чтобы удалить его, но не сам$build
каталог. Таким образом, если вы пропустили постороннее,*
то получится неустановленное значение,rm -rf /
которое по умолчанию большинство реализаций rm за последнее десятилетие откажется выполнять (если вы не отключите эту защиту с помощью--no-preserve-root
опции GNU rm ).Пропуск трейлинга
/
также приведетrm ''
к появлению сообщения об ошибке:Это работает, даже если ваша команда rm не реализует защиту для
/
.источник
Вы думаете с точки зрения языка программирования, но bash - это язык сценариев :-) Итак, используйте совершенно другую парадигму инструкций для совершенно другой языковой парадигмы.
В этом случае:
Так
rmdir
как откажется удалить непустую директорию, сначала нужно удалить участников. Вы знаете, что это за члены, верно? Они, вероятно, являются параметрами вашего сценария или получены из параметра, поэтому:Теперь, если вы поместите туда какие-нибудь другие файлы или каталоги, например временный файл или что-то, чего не должно быть, вы получите
rmdir
ошибку. Обращайтесь с этим правильно, тогда:Такой образ мышления хорошо послужил мне, потому что ... да, был там, сделал это.
источник
make clean
будет использовать некоторые символы подстановки (если только очень старательный компилятор не создаст список каждого файла, который он создает). Кроме того, мне кажется, чтоrm -rf ${build}/${parameter}
только немного сдвинул вопрос вниз.make
. Написание ответа, который применим только к этому,make
было бы очень конкретным и удивительным предположением. Мой ответ работает для случаевmake
, отличных от разработки программного обеспечения, кроме вашей конкретной проблемы новичка, которая возникла при удалении ваших материалов.