удалить только файлы и каталоги запросов

#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 сортирует результаты от самых глубоких каталогов до самых мелких.