Как архивировать рекурсивно все подкаталоги, которые только содержат текстовые файлы

Ответ Michael Mrozek будет работать, если Вы не будете заботиться об удалении команд от текущей сессии. Если Вы делаете, необходимо записать в файл истории прежде, чем сделать операции в его сообщении путем выполнения history -a.

Кроме того, после удаления записей, которые Вы хотите из своего файла истории, можно перезагрузить его путем издания history -c, затем history -r.

3
08.12.2013, 22:54
4 ответа

Этот сценарий заархивирует, затем дополнительно удаляют все папки, содержащие "*.txt" файлы и ничто иное.

folders=$(find . -type d -exec sh -c 'cd "$1";[ "$(ls *.txt 2>/dev/null)" ] \
          && [ -z "$(ls -ad * | grep -v '\.txt$')" ] && echo "$1"' sh {} \;)

echo "$folders" | zip -r@ archive && echo "$folders" | while read folder; do
  echo "will remove $folder"
  # Uncomment next line for the folder to be removed
  # rm -rf "$folder"
done

Править: вот решение, которое создает отдельные zip-файлы:

find . -depth -type d -exec sh -c '
cd "$1" || exit
[ "$(ls ./*.txt 2>/dev/null)" ] &&
[ -z "$(ls -ad ./* | grep -v '\.txt$')" ] &&
(
  b=$(basename "$1")
  cd ..
  zip -r "$b.zip" "$b" && rm -rf "$b"
)' sh {} \;
4
27.01.2020, 21:14
  • 1
    , Если я понимаю это правильно, это архивирует каждый текстовый файл. Я хочу архивировать всю папку---с условием, что это только содержит текстовые файлы---, и затем удалите исходную папку. –  mikuszefski 08.12.2013, 15:53
  • 2
    Вы имеете в виду, содержит ли папка и .txt и не .txt файлы, это не должно быть заархивировано вообще? –  jlliagre 08.12.2013, 16:00
  • 3
    Да, несколько как это. Только папки, которые являются окончательно папками данных, т.е. исключительно содержащий *.txt файлы. Можно было бы даже думать о добавлении дополнительного (если больше, чем приблизительно 5 МБ). Я предполагаю, что это не сделано в одной строке. –  mikuszefski 08.12.2013, 16:04
  • 4
    Так папка, содержащая много .txt файлов, но также и или подпапка или файл под названием foo.jpg должны быть проигнорированы? –  jlliagre 08.12.2013, 16:12
  • 5
    , о, нет. Я вижу свое отсутствие точности здесь. Папки, которые будут заархивированы, больше не содержат подпапки. Таким образом, у меня есть сложное дерево файла и если одно ответвление заканчивается в папке, которая содержит только *.txt, который должен быть заархивирован. –  mikuszefski 08.12.2013, 16:24

Вот более простой подход: Использовать find получить список каталогов; в каждом каталоге проверьте если список .txt файлы совпадают со списком всех файлов. Если это, идите вперед и архивируйте его.

Это сделано намного легче тем, что заархивированные папки не могут содержать подпапки. Я предполагаю, что ни один из Ваших файлов или папок не начинается с точки; если это не верно, сообщите мне, и я добавлю необходимые проверки.

for DIR in `find . -type d -print`
do
    TXT=`echo "$DIR"/*.txt`
    ALL=`echo "$DIR"/*`
    if [ "$TXT" == "$ALL" ]
    then
        echo only txt: $DIR
        # zip "$DIR"
    fi
done
1
27.01.2020, 21:14
  • 1
    Обратите внимание что из-за for DIR in $(find …) только работы, если ни одни из имен каталогов не содержат пробел или \[*?. –  Gilles 'SO- stop being evil' 09.12.2013, 00:05

Следующий отрывок пересекает все подкаталоги текущего каталога. Если это находит каталог, содержащий только подкаталоги или файлы с .txt расширение, это архивирует каталог и удаляет его.

find . -type d -exec sh -c '
  if [ -z "$(find "$0" ! -type d ! -name "*.txt" | head -n 1)" ]; then
    zip -r "$0.zip" "$0" && rm -r "$0"
  else
    exit 1
  fi
' {} \; -prune
1
27.01.2020, 21:14
  • 1
    Ай. Я боюсь, что существует несколько дизайнов и синтаксических ошибок в Вашем сценарии. find не может возвратить пустую строку с -type d учитывая факт текущий каталог будет всегда соответствовать. Вы имеете .txt тест инвертировал (должен быть ! -name "*.txt"). Zip-файл будет содержать целую иерархию, в то время как OP мог бы предпочесть только единственный каталог. Вы оба отсутствовали для передачи {} и пропущенный для окончания -exec действие. –  jlliagre 09.12.2013, 13:05

Следующий сценарий рекурсивно находит каталоги и выполняет zip на них, если они содержат только .*txt файлы. Это работает как это: все подкаталоги проверяются с шаблоном на non-txt файлы и шаблоном для txt-файлов. Если существуют какие-либо txt подобранные файлы и нет никаких других типов подобранных файлов, каталог заархивирован и уничтожен.

#!/bin/bash

shopt -s dotglob nullglob extglob
for dir in $(find "$1" -type d); do
    non_txt=("$dir"/!(*.txt)); txt=("$dir"/+(*.txt))
    if ((${#txt[@]} && ! ${#non_txt[@]})); then
        zip -r "$dir.zip" "$dir" && rm -r "$dir"
    fi
done

Другой вариант, который рассматривает имена каталогов с \[*? символы в них:

#!/bin/bash

find "$1" -depth -type d -exec sh -c '
    shopt -s dotglob nullglob extglob
    non_txt=("$1"/!(*.txt)); txt=("$1"/+(*.txt))
    if ((${#txt[@]} && ! ${#non_txt[@]})); then
        zip -r "$1.zip" "$1" && rm -r "$1"
    fi
' sh {} \;
0
27.01.2020, 21:14
  • 1
    Отметьте это for dir in $(find …) только работы, если ни одни из имен каталогов не содержат пробел или \[*?. –  Gilles 'SO- stop being evil' 09.12.2013, 00:04
  • 2
    @Gilles, Как я изменил бы его для обработки тех случаев? Вы могли сделать редактирование, возможно? –   09.12.2013, 00:06
  • 3
    Устойчивый путь состоит в том, чтобы использовать -exec основной, как в ответе jlliagre. –  Gilles 'SO- stop being evil' 09.12.2013, 00:09
  • 4
    @Gilles я представил другое различное использование -exec, хотя я не могу действительно сказать два результата независимо, рассмотрев каталоги с этими символами, кроме пробела, который не обрабатывается хороший for i in $(find …). –   09.12.2013, 01:05
  • 5
    Для GNU находит (и нестаринный удар), можно использовать find ... -print0 | while read -d $'\0' i; do stuff with "$i"; done - должно быть пуленепробиваемым, и я думаю, что это выглядит более опрятным, чем зубрежка все это в - должностное лицо. –  evilsoup 09.12.2013, 02:29

Теги

Похожие вопросы