Переместить папки, содержащие более 100 файлов?

В ответе Майкла отсутствует одна вещь :очистка удаляет только архивные файлы журналов, а не активные. Чтобы избавиться от всего, вам нужно сначала повернуть файлы, чтобы последние записи были перемещены в неактивные файлы.

Таким образом, полный ответ на удаление всех записей выглядит так:

sudo journalctl --rotate
sudo journalctl --vacuum-time=1s

(Обратите внимание, что вы не можете объединить это в одну команду journalctl.)

Кстати, в некоторых дистрибутивах journald настроен так, что он записывает логи на диск (/var/log/journal), а другие хранят логи в памяти(/run/log/journal).Я ожидаю, что в некоторых случаях может быть необходимо сначала использовать sudo journalctl --flush, чтобы удалить все.

Если в вашей версии нет --rotate, вы можете использовать аргумент --sinceдля фильтрации записей:

--since "2019-01-30 14:00:00"
--since today

1
14.08.2020, 19:22
5 ответов

Вы можете перебрать каждый каталог по очереди, подсчитать количество содержащихся в нем файлов, а затем переместить его... куда-нибудь. Например,

for dir in./*/
do
    count=$(find "$dir" -maxdepth 1 -type f -printf "x\n" | wc -l)    # Count the number of files in this subdirectory
    [ $count -gt 100 ] && echo mv "$dir"                              # Output a message if we have enough
done

Вы можете ввести это прямо в подсказке (, вы получите дополнительную подсказку, >, после первой строки до последней )или сохраните ее в файле сценария и запустите.

1
18.03.2021, 23:12

Чтобы подсчитать количество не -скрытых имен в каталоге dir, вы можете использовать

set -- dir/*

Это расширяет глобус *в каталоге и устанавливает позиционные параметры для результирующих имен. Если шаблон совпал с чем-либо , счет будет в $#.

Чтобы выполнить итерацию по всем каталогам в некотором каталоге верхнего -уровня top-dir, подсчитайте количество имен в каждом и сделайте что-нибудь с каталогами, которые содержат более 100 имен:

for subdir in top-dir/*/; do
    set -- "$subdir"/*
    if [ -e "$1" ] && [ "$#" -gt 100 ]; then
        # do something to "$subdir"
    fi
done

В оболочке bashустановка параметра оболочки nullglobизбавляет от необходимости проверять, удалось ли команде setсопоставить какие-либо имена вообще (, поскольку шаблон был бы полностью удален, если бы нет совпадений вместо того, чтобы оставаться нераскрытым ).

shopt -s nullglob

for subdir in top-dir/*/; do
    set -- "$subdir"/*
    if [[ $# -gt 100 ]]; then
        # do something to "$subdir"
    fi
done

Дополнительная установка параметра оболочки dotglobприведет к тому, что каждый шаблон в коде также будет соответствовать скрытым именам.

В любом фрагменте кода выше комментарий «сделайте что-нибудь с "$subdir"» можно заменить на все, что вам нужно сделать с этими подкаталогами. Например,. уберите их, используйте

mv "$subdir" some/other/dir

Что переместит их в каталог some/other/dir.

2
18.03.2021, 23:12

Сzsh:

mv -- *(/Fe['()(($# > 100)) $REPLY/*(N^-/)']) /dest/

Будет перемещен в /dest/не -скрытые подкаталоги текущего рабочего каталога, которые содержат более 100 записей¹, которые не являются ни скрытыми, ни типом каталога (, который, как я предполагаю, вы подразумеваете под файлом).

При этом используются квалификаторыzshglob((/Fe...)и (N...)выше ), которые дополнительно выбирают подходящие файлы на основе других критериев, а не их имени.

  • /:выберите только файлы типа из каталога . Здесь (в отличие от */glob )тип определяется до разрешения символической ссылки, что, вероятно, здесь предпочтительнее, поскольку перемещение символических ссылок часто их ломает ).
  • F:выбирает полные файлы в качестве оптимизации (для каталогов, что означает непустые каталоги)
  • e[code]:выбрать на основе результата интерпретации code, где $REPLYсодержит рассматриваемый в данный момент файл.

Это codeздесь ()(($# > 100)) $REPLY/*(N^-/).

() <body> <args>— встроенная функция. Здесь тело((($# > 100)))проверяет, что количество аргументов больше 100. Аргументы являются расширением глобуса $REPLY/*(N^-/)снова с использованием квалификаторов глобуса :

  • N:nullglob :этот глобус будет расширяться до вообще без аргумента вместо ошибки, когда нет соответствующего файла.
  • ^:отрицает следующие квалификаторы.
  • -/аналогичен /выше, за исключением того, что -заставляет следующие квалификаторы (здесь/)применять после разрешения символической ссылки . Итак, здесь мы подсчитываем файлы, которые имеют не типа каталог после разрешения символической ссылки. Вы можете заменить ^-/на ., чтобы считать только обычные файлы (, исключая все другие типы файлов, такие как сокеты, fifo, каталоги, символические ссылки... ), или -.для обычных файлов и символических ссылок на обычные файлы.

Чтобы также учитывать скрытые каталоги/файлы, добавьте квалификатор D(к одному или обоим внешнему и внутреннему шаблонам ).

Чтобы также рекурсивно подсчитывать файлы в подкаталогах, замените второй *на**/*(или ***/*для обхода символических ссылок при спуске по дереву каталогов ).

Вы можете оптимизировать его, изменив codeна:

()(($#)) $REPLY/*(NoN^-/[101])

Это использование oNдля отключения сортировки файлов, порядок которых нам не важен, а глобус расширяется только до 101 st соответствующего файла, который мы просто проверяем на присутствие с помощью(($#))(количество аргументов не -ноль ).


¹ Остерегайтесь, что несколько записей могут ссылаться на один и тот же файл, например, когда они соединены жесткими или символическими ссылками. Подсчет количества уникальных файлов будет другим упражнением

1
18.03.2021, 23:12

Мы формируем цепочку проверок для определения действительного файла в каталоге без символической ссылки. каталоги могут быть скрыты или нет, файлы могут быть скрыты или нет.

Изменить количество файлов в пороговой переменной и путь назначения в папке назначения _Переменная. Удалите эхо, как только все будет в порядке.

Исправлена ​​ошибка, из-за которой тест каталога с символической ссылкой завершался ошибкой для имени с косой чертой в конце и командой mv, как указал Стивен.

$ threshold=100
$ dest_dir=final/resting/place
$ for d in./*/./..?*/./.[!.]*/; do
    [ ! -L "${d%/}" ] && [ -d "$d" ] && \
    [ "$(cd "$d" && find.//. ! -name. -prune -type f | grep -cF.//.)" -gt "$threshold" ] \
     && echo mv -- "$d" "$dest_dir";
  done

Мы можем свести к минимуму количество перемещений, если будем использовать xargs GNU, которые ограничивают аргументы NUL \0char, и последние версии mv, которые поддерживают -параметр t (target dir):

$ threshold=100
$ dest_dir=final/resting/place
$ for d in./*/./..?*/./.[!.]*/; do
    [ ! -L "${d%/}" ] && [ -d "$d" ] && \
    [ "$(cd "$d" && find.//. ! -name. -prune -type f | grep -cF.//.)" -gt "$threshold" ] \
     && printf '%s\0' "$d"
  done | xargs -r0 -t mv -t "$dest_dir"
0
18.03.2021, 23:12

Помимо подходов к оболочке, вы также можете использовать конвейер, состоящий из findкоманды, созданной для выбора папок/файлов для проверки и передачи их сценарию Awk, который выполняет фильтрацию для окончательного xargs, который объединяет фактический mvза минимально возможное количество запусков. Это также может быть сценарий оболочки, но Awk обычно лучше и быстрее обрабатывает текст.

В приведенном ниже примере используются инструменты GNU, предназначенные для работы с nul -вводом-выводом с разделителями, чтобы поддерживать имена файлов со встроенными символами новой строки:

find. -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' -print0 \
    | LC_ALL=C gawk -F/ -v RS='\0' -v ORS='\0' \
        '{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; print $2}}' \
    | xargs -r0 mv -t dest/

По сравнению с решениями на основе чистой оболочки этот конвейер должен быть менее требовательным к ресурсам, поскольку он не выполняет никакой буферизации во время обработки, поэтому на него практически не влияет любое количество папок и файлов.

Обратите внимание на сравнение n>100в скрипте awk:, где вы можете настроить порог по желанию.

Ожидается, что этот конвейер будет запускаться из каталога, содержащего папки для проверки, поскольку он использует «голый» find.. Тем не менее, вы можете легко сделать его общим, просто добавив часть find.к части cd -- "${topdir:-.}" && , чтобы указать начальный каталог с помощью пользовательской переменной оболочки $topdir, по умолчанию равной ., то есть текущему каталогу.

Справедливый эквивалент такого конвейера с использованием инструментов BSD, за исключением поддержки новых строк в именах файлов из-за ограничений, присущих инструментам BSD, может быть следующим:

find -E. -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' \
    | LC_ALL=C awk -F/ -v q=\' \
        '{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; gsub(q, q"\\"q q, $2); print q$2q}}' \
    | xargs sh -c '${1:+mv -- "$@" dest/}' --

, который по сути такой же, как версия инструментов GNU, за исключением различных опций -print0, -zи -0, а также дополнительной операции gsub()сценария awkдля кавычек разделительных символов (символы " ' <space>), возможно, присутствующие в именах файлов, как это необходимо POSIX xargsдля использования.

Этот последний конвейер должен корректно работать в любой системе BSD, при условии, что ни один проверяемый путь (к папкам и файлам )не содержит новых строк.

Говоря о совместимости с POSIX,конвейер инструментов BSD также должен работать в любой системе POSIX, за исключением команды find, поскольку в POSIX нет предложений -maxdepthи -regex. Эквивалент POSIX для этого findможет быть похож на:

# replace the find command of the BSD tools version, up to and including the trailing backslash character
find. \( -path '*/*/*' ! -path '*/*/*/*' -type f \) -o \( -path '*/*' ! -path '*/*/*' -type d \) ! -name '.*' \

Выражение findтакже создано для облегчения работы сценария awkи выбирает обычные файлы на третьем уровне иерархии каталогов (, где уровень 1 равен .), плюс только каталоги на втором уровне. Не имея более мощных предложений, доступных в BSD и GNU find, я получаю тот же результат, играя в предложения -path.

Наконец, обратите внимание, что эти конвейеры явно игнорируют скрытые файлы через предложение ! -name '.*'до find, и они рассматривают только обычные файлы через предложение -type f(, следовательно, исключая, например,. символические ссылки ), потому что это казалось наиболее разумным выбором в соответствии с вашим вопросом, но если вы хотите учитывать скрытые файлы и/или вложенные -папки, символические ссылки и, возможно, специальные файлы (именованные каналы, именованные сокеты, и т. д. ), которые могут присутствовать внутри папок, вы можете просто убрать соответствующее предложение или, возможно, настроить их с помощью дополнительных предложений для команды find. В этом последнем случае обратите внимание на то, чтобы заставить findвсегда выдавать имена одиночных папок 1 , потому что эти одиночные имена являются «сигналом», используемым сценарием awkдля определения того, что следующие имена в папке, отличной от предыдущих имен 2 .


1. только те, что на втором уровне

2. для лучшей производительности я использовал проверку истинности/ложности вместо сравнения строк, см. переменную dв скрипте awk

0
18.03.2021, 23:12

Теги

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