#linux #bash #shell #rm
#linux #bash #оболочка #rm
Вопрос:
Поскольку я удалял много устаревших файловых деревьев на компьютере с Linux, мне было интересно, есть ли простой способ рекурсивно удалять файлы, запрашивая только каталоги.
Я мог бы использовать rm -ri
, но там много файлов, на которые было бы очень неприятно отвечать за каждый из них. Что действительно важно для меня, так это то, что в папках запрашивается больше контроля над тем, что происходит.
Я не эксперт по bash, поэтому я спрашиваю, есть ли простой способ сделать это.
Вот моя попытка с длинным сценарием bash:
#!/bin/bash
promptRemoveDir()
{
fileCount=$(ls -1 $1 | wc -l)
prompt=1
while [ $prompt == 1 ]
do
read -p "remove directory: $1($fileCount files) ? [yl]: " answer
case $answer in
[yY])
rm -r $1
prompt=0
;;
l)
echo $(ls -A $1)
;;
*)
prompt=0
;;
esac
done
}
removeDir()
{
if [ "$(ls -A $1)" ]
then dirs=$(find $1/* -maxdepth 0 -type d)
fi
if [[ -z $dirs ]]
then
promptRemoveDir $1
else
for dir in $dirs
do
removeDir $dir
done
promptRemoveDir $1
fi
}
for i in $*
do
if [ -d $i ]
then
removeDir $i
else
rm $i
fi
done
Комментарии:
1. Что значит запрашивать перед удалением всех файлов в каталоге?
2. Почему вы использовали
rm -ri
?-i
опция запрашивает перед каждым удалением. Вы не можете использоватьrm -r
?3. @Jidder я имею в виду запрос подтверждения перед удалением каталога и его файлов. однако, если каталог уже содержит один или несколько каталогов, сначала необходимо проверить подкаталоги, чтобы не пропустить ни один из них. Проверьте рекурсивную функцию RemoveDir, чтобы помочь понять.
4. @SanketParmar Я использовал -ri для рекурсивного удаления в файловом дереве. Я не мог использовать -i только потому, что для этого потребовалось бы вводить в каждый каталог вручную, начиная с самых глубоких каталогов в дереве файлов. Предыдущий сценарий автоматизировал это поведение.
Ответ №1:
Если я правильно понял ваш вопрос, это должно сработать
Dirs=$(find . -type d)
Удаляет только файлы в указанных каталогах
for i in "$Dirs"; do read -p "Delete files in "$i": ";if [[ $REPLY == [yY] ]]; then find $i -maxdepth 1 -type f | xargs -0 rm ; fi ;done
Если вы хотите также удалить папки, это будет считываться из самого нижнего каталога (ни одного ниже него) вверх.
for i in $(echo "$Dirs" | sed '1!G;h;$!d' ); do read -p "Delete files in $i: ";if [[ $REPLY == [yY] ]]; then rm -r "$i"; fi ;done
Комментарии:
1. сбой, если имена каталогов содержат пробелы. укажите «$ Dirs» после for и $i после rm.
2. Таким образом, вы бы удалили некоторые каталоги внутри каталогов, прежде чем запрашивать подтверждение
3. @dletozeun удаляет только файлы
4. @Jidder я вижу, но я хочу удалить файлы и каталоги. Однако ваше редактирование со вторым фрагментом кода работает очень хорошо, спасибо!
Ответ №2:
Вот упрощенная версия от меня. Нет необходимости использовать ls
и find
.
#!/bin/bash
shopt -s nullglob
shopt -s dotglob
function remove_dir_i {
local DIR=$1 ## Optional. We can just use $1.
local SUBFILES=("$DIR"/*) FILE
for (( ;; )); do
read -p "Remove directory: $DIR (${#SUBFILES[@]} files)? [YNLQ]: "
case "$REPLY" in
[yY])
echo rm -fr "$DIR"
return 0
;;
[nN])
for FILE in "${SUBFILES[@]}"; do
if [[ -d $FILE ]]; then
remove_dir_i "$FILE" || return 1
# else
# ## Apparently we skip deleting a file. If we do this
# ## we could actually simplify the function further
# ## since we also delete the file at first loop.
# # echo "Removing file "$FILE.""
# # rm -f "$FILE"
fi
done
return 0
;;
[lL])
printf '%sn' "${SUBFILES[@]}"
;;
[qQ])
return 1
;;
# *)
# echo "Please answer Y(es), N(o), L(ist) or Q(uit)."
# ;;
esac
done
}
for FILE; do
if [[ -d $FILE ]]; then
remove_dir_i "$FILE"
else
# echo "Removing file "$FILE.""
echo rm -f "$FILE"
fi
done
Удалите echo
из rm
команд, когда вы уверены, что это уже работает. Тест:
rm -f /tmp/tar-1.27.1/ABOUT-NLS
rm -f /tmp/tar-1.27.1/acinclude.m4
rm -f /tmp/tar-1.27.1/aclocal.m4
rm -f /tmp/tar-1.27.1/AUTHORS
Remove directory: /tmp/tar-1.27.1/build-aux (12 files)? [YNLQ]: n
Remove directory: /tmp/tar-1.27.1/build-aux/snippet (5 files)? [YNLQ]: n
rm -f /tmp/tar-1.27.1/ChangeLog
rm -f /tmp/tar-1.27.1/ChangeLog.1
rm -f /tmp/tar-1.27.1/config.h.in
rm -f /tmp/tar-1.27.1/configure
rm -f /tmp/tar-1.27.1/configure.ac
rm -f /tmp/tar-1.27.1/COPYING
Remove directory: /tmp/tar-1.27.1/doc (25 files)? [YNLQ]: n
Remove directory: /tmp/tar-1.27.1/gnu (358 files)? [YNLQ]: n
Remove directory: /tmp/tar-1.27.1/gnu/uniwidth (2 files)? [YNLQ]: n
rm -f /tmp/tar-1.27.1/INSTALL
Remove directory: /tmp/tar-1.27.1/lib (19 files)? [YNLQ]:
...
Комментарии:
1. Спасибо, но я думаю, что все еще существует проблема, на которую я указывал в других ответах. Из вашего вывода: Удалить каталог: /tmp / tar-1.27.1/build-aux затем: Удалить каталог: /tmp / tar-1.27.1 /build-aux /snippet [YNLQ]: Если каталог build-aux будет удален, build-aux / snippet больше не будет существовать, и онслишком поздно для подтверждения удаления.
2. Вы фактически удаляете файлы в первом цикле. Как насчет других файлов, найденных во время рекурсии — планируете ли вы их также удалить? Если бы подкаталоги были выбраны как не удаляемые, вас все равно спросили бы, будет ли удален родительский каталог, или по умолчанию вы бы их не трогали?
3. Некоторые незначительные изменения, внесенные мной, вероятно, уже исправили бы это.
Ответ №3:
На самом деле я только что наткнулся на опцию -depth команды find, которая является именно тем, что я искал. Я не могу поверить, что я только что пропустил это:
-глубокая обработка содержимого каждого каталога перед самим каталогом. Действие -delete также подразумевает -depth .
Так похоже на код @ Jidder, я могу написать это:
dirs=$(find ./test_script -depth -type d); for i in $dirs; do read -p "Delete files in $i? " REPLY; if [[ $REPLY == [yY] ]]; then rm -r $i; fi; done;
И для большей удобочитаемости:
dirs=$(find ./test_script -depth -type d)
for i in $dirs
do
read -p "Delete files in $i? " REPLY
if [[ $REPLY == [yY] ]]
then rm -r $i
fi
done;
Комментарии:
1. Это не будет рекурсивным, хотя? Он будет обрабатывать только каталоги непосредственно под
./test_script
ним, и вы не будете знать, содержат ли они каталоги дальше. Это то, что вы хотели?2. Поведение команды find по умолчанию заключается в поиске по всему искомому дереву каталогов. Так что да, в этом случае он дает мне все каталоги в каталоге «. / test_script». опция -depth сортирует результаты от самых глубоких каталогов до самых мелких.