В закладочках нашёл любопытный сервис, который очень полезен линуксоидам - конструктор для консольной команды FIND. Это такая типичная линуксовая утилита, у которой 100500 параметров и возможностей, ключи которой невозможно запомнить раз и навсегда. У меня огромная шпаргалка по find на все случаи жизни, чтобы не приходилось заново вспоминать, как реализовать тот или иной поиск.
Кое-что необычное по find уже публиковал на канале:
- очистка старых файлов
- поиск дубликатов файлов
- утилита fd для упрощения поиска через find
Сервис называется find-command-generator. С его помощью узнал, что find очень просто может находить файлы, которые принадлежат несуществующему пользователю или группе, или обоим одновременно. Бывает нужно такие найти:
Программисты любят ставить 777 без разбора. Постоянно с этим сталкиваюсь. Когда у них что-то работает не так, они первым делом права доступа 777 ставят, а потом дальше разбираются.
#bash #terminal #find
Кое-что необычное по find уже публиковал на канале:
- очистка старых файлов
- поиск дубликатов файлов
- утилита fd для упрощения поиска через find
Сервис называется find-command-generator. С его помощью узнал, что find очень просто может находить файлы, которые принадлежат несуществующему пользователю или группе, или обоим одновременно. Бывает нужно такие найти:
# find . -nogroup -nouser
Сервис пригодится, когда нужно найти что-то с параметрами, типа ограничения по датам изменения или доступа, размеру, правам доступа и т.д. Типичная история, когда надо проверить директорию веб сервера и снять с файлов все права на исполнение:# find /web/site/www -type f -name "*" -perm u+x -exec chmod 664 {} \;
Программисты любят ставить 777 без разбора. Постоянно с этим сталкиваюсь. Когда у них что-то работает не так, они первым делом права доступа 777 ставят, а потом дальше разбираются.
#bash #terminal #find
Давно не было заметок по полезным консольным командам под bash. В комментариях к заметке о find меня попросили поделиться своими шпаргалками для этой утилиты. Собрал наиболее универсальные и часто используемые конструкции.
Вывести только имена файлов. Поиск файлов пойдёт рекурсивно от той директории, где будет запущена команда:
Переместить найденные по маске файлы из одной директории в другую:
Найти все файлы по маске и сжать:
Переименовать все найденные файлы:
Подробный список файлов размером больше 100М с помощью ls:
Поиск файлов по содержимому (header.php):
Посчитать количество найденных файлов:
Найти файлы в определённом временном интервале:
Потом их можно сжать, переместить и т.д, как было показано в предыдущих примерах.
Искать без учёта регистра:
Поиск файлов, которые менялись сколько то дней назад и более:
или в течении последних 5 дней, но не позже:
или в определённом промежутке в минутах (10-20 минут назад):
Cортировка по дате изменения:
и обратная сортировка:
Захватил наиболее типовые ситуации, с которыми чаще всего приходится сталкиваться в консоли или скриптах автоматизации.
#bash #terminal #find
Вывести только имена файлов. Поиск файлов пойдёт рекурсивно от той директории, где будет запущена команда:
# find | awk -F '/' '{print $NF;}'
Переместить найденные по маске файлы из одной директории в другую:
# find /mnt/backup/*site.ru* -type f -exec mv {} /web/sites/ \;
Найти все файлы по маске и сжать:
# find /data/1c-bases-backup -type f -name 1Cv8.1CD -exec gzip '{}' \;
Переименовать все найденные файлы:
# find /backup/sql -type f -name "*.sql.gz" -exec mv {} {}.old \;
Подробный список файлов размером больше 100М с помощью ls:
# find /data -type f -size +100M -exec ls -lh '{}' \;
Поиск файлов по содержимому (header.php):
# find /web/site.ru -type f -exec grep -i -H "header.php" {} \;
Посчитать количество найденных файлов:
# find /home/user -type f | wc -l
Найти файлы в определённом временном интервале:
# find /mnt/zz_archive -type f -newermt '2022-01-01 00:01' ! -newermt '2022-01-31 23:59'
Потом их можно сжать, переместить и т.д, как было показано в предыдущих примерах.
Искать без учёта регистра:
# find /data -type f -iname FILE_NAME
Поиск файлов, которые менялись сколько то дней назад и более:
# find /data -type f -mtime +30
или в течении последних 5 дней, но не позже:
# find /data -type f -mtime -5
или в определённом промежутке в минутах (10-20 минут назад):
# find /data -type f -mmin -20 -mmin +10
Cортировка по дате изменения:
# find /data -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort -r
и обратная сортировка:
# find /data -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort
Захватил наиболее типовые ситуации, с которыми чаще всего приходится сталкиваться в консоли или скриптах автоматизации.
#bash #terminal #find
Утилита find и время изменения файлов
Когда пишу скрипты с использованием find, регулярно натыкаюсь на одну и ту же неточность в своём понимании некоторых популярных параметров. Конкретно вот этих:
◽ mtime – время последнего изменения файла
◽ ctime – время создания файла
◽ atime – время последнего доступа к файлу
Когда вводишь команду:
ожидаешь найти все каталоги, созданные более суток назад, чтобы удалить их и освободить место на диске. Я специально взял именно сутки, так как тут сразу наглядно видно разночтение в ожидании и реальности. Find в таком виде покажет каталоги, созданные более трех суток назад.
Вот наглядный пример:
Директория 2022-06-05 старше суток, но в вывод find не попала. По факту параметр +1 означает всё, что старше двух суток. А для поиска старше суток, необходимо использовать +0.
Это связано с тем, что find определяет, как много 24-часовых периодов назад директория была создана. Любая дробная часть игнорируется, поэтому для соответствия -ctime +1 директория должна быть создана по крайней мере два дня назад.
Когда используешь большие интервалы, не обращаешь на такую мелочь внимания. А когда у тебя где-то на промежуточном хранилище не хватает свободного места и тебе надо точно рассчитать время хранения, такие нюансы становятся критичны.
Я с периодичностью раз в год-два разбираюсь с этим нюансом. Встречается редко, но когда наткнёшься, начинаешь вспоминать, как там find интерпретирует эти интервалы. Решил записать, может больше не буду забывать, но это вряд ли 🤷🏻♂️.
#bash #find #terminal
Когда пишу скрипты с использованием find, регулярно натыкаюсь на одну и ту же неточность в своём понимании некоторых популярных параметров. Конкретно вот этих:
◽ mtime – время последнего изменения файла
◽ ctime – время создания файла
◽ atime – время последнего доступа к файлу
Когда вводишь команду:
# find /mnt/backup -maxdepth 1 -type d -ctime +1
ожидаешь найти все каталоги, созданные более суток назад, чтобы удалить их и освободить место на диске. Я специально взял именно сутки, так как тут сразу наглядно видно разночтение в ожидании и реальности. Find в таком виде покажет каталоги, созданные более трех суток назад.
Вот наглядный пример:
# date +"%Y-%m-%d %H-%M"
2022-06-06 19-45
# ls -lh
drwxr-xr-x 3 root root 4.0K Jun 2 17:09 2022-06-02
drwxr-xr-x 3 root root 4.0K Jun 3 03:03 2022-06-03
drwxr-xr-x 3 root root 4.0K Jun 4 03:03 2022-06-04
drwxr-xr-x 3 root root 4.0K Jun 5
03:03 2022-06-05
drwxr-xr-x 3 root root 4.0K Jun 6 03:03 2022-06-06
# find /mnt/backup -maxdepth 1 -type d -ctime +1
/mnt/backup/2022-06-04
/mnt/backup/2022-06-03
/mnt/backup/2022-06-02
Директория 2022-06-05 старше суток, но в вывод find не попала. По факту параметр +1 означает всё, что старше двух суток. А для поиска старше суток, необходимо использовать +0.
Это связано с тем, что find определяет, как много 24-часовых периодов назад директория была создана. Любая дробная часть игнорируется, поэтому для соответствия -ctime +1 директория должна быть создана по крайней мере два дня назад.
Когда используешь большие интервалы, не обращаешь на такую мелочь внимания. А когда у тебя где-то на промежуточном хранилище не хватает свободного места и тебе надо точно рассчитать время хранения, такие нюансы становятся критичны.
Я с периодичностью раз в год-два разбираюсь с этим нюансом. Встречается редко, но когда наткнёшься, начинаешь вспоминать, как там find интерпретирует эти интервалы. Решил записать, может больше не буду забывать, но это вряд ли 🤷🏻♂️.
#bash #find #terminal
Linux всегда меня восхищал и радовал простыми решениями по возможностям работы с текстовыми файлами через командную строку. Слово простые можно было бы взять в кавычки, так как в реальности не просто изучить синтаксис подходящих консольных утилит. Но никто не мешает найти готовое выражение и использовать его. В Windows чаще всего можно решить эти же задачи с помощью своих программ, но обычно это занимает больше времени.
Самый простой пример того, о чём я говорю — автоматическая замена определённого текста в заданных файлах. Мне лично чаще всего это нужно, когда что-то делаешь с исходниками сайтов. Помню, как свои первые сайты делал более 20-ти лет назад на чистом html в Dreamweaver. При этом страниц там было сотни. Обновлял вручную копипастом. Это было тяжело, но в то время большинство сайтов были статичными, так как бесплатных хостингов на php не существовало. Но это я отвлёкся, заметка планируется про другое.
Допустим, у вас есть какой-то большой сайт на php и вам надо во всех файлах заменить устаревшую функцию на новую. В общем случае замену текста можно сделать с помощью sed, примерно так:
Или посложнее пример с вырезанием вредоносного куска кода из всех файлов, которые заразил какой-то вирус. Допустим, это некий код следующего содержания:
Текста между тэгами script может быть много, поэтому искать проще всего по этому тэгу и началу строки с function aeaab19d(a).
Тут я использую ключ -r для поддержки регулярных выражений, конкретно
Можно ещё усложнить и выполнить замену кода между каких-то строк. Для усложнения возьмём какой-нибудь XML:
Заменим user01 на user02
Тут важны круглые скобки и \1 и \2. Мы в первой части выражения запомнили текст в круглых скобках, а во второй части его использовали — сначала первую скобку, потом вторую.
Это были примеры для одиночных файлов, а теперь добавляем сюда find и используем sed на любом наборе файлов, который найдёт find.
Добавляем к sed ключ -i для того, чтобы он сразу изменял файл. Кстати, для find наиболее популярные примеры можете посмотреть через тэг #find.
Очень аккуратно выполняйте массовые действия. Сначала всё отладьте на тестовых файлах. Потом сделайте бэкап исходных файлов. И только потом выполняйте массовые изменения. И будьте готовы быстро всё откатить обратно.
Примеры рекомендую записать. Если надо быстро что-то сделать, то сходу правильно регулярку вы так просто не наберёте. К тому же в таком использовании есть свои нюансы. К примеру, я так и не смог победить команду sed, которая удаляет весь код <script>, если внутри есть переход на новую строку. Вроде бы легко найти, как заставить
Не забывайте про сервисы, которые помогают отлаживать регулярки. Собрал их в отдельной заметке.
#linux #bash #script
Самый простой пример того, о чём я говорю — автоматическая замена определённого текста в заданных файлах. Мне лично чаще всего это нужно, когда что-то делаешь с исходниками сайтов. Помню, как свои первые сайты делал более 20-ти лет назад на чистом html в Dreamweaver. При этом страниц там было сотни. Обновлял вручную копипастом. Это было тяжело, но в то время большинство сайтов были статичными, так как бесплатных хостингов на php не существовало. Но это я отвлёкся, заметка планируется про другое.
Допустим, у вас есть какой-то большой сайт на php и вам надо во всех файлах заменить устаревшую функцию на новую. В общем случае замену текста можно сделать с помощью sed, примерно так:
# sed 's/old_function/new_function/g' oldfilename > newfilename
Или посложнее пример с вырезанием вредоносного куска кода из всех файлов, которые заразил какой-то вирус. Допустим, это некий код следующего содержания:
<script>function aeaab19d(a)...................</script>
Текста между тэгами script может быть много, поэтому искать проще всего по этому тэгу и началу строки с function aeaab19d(a).
# sed -r 's/<script>function aeaab19d\(a\).*?<\/script>//' test.php
Тут я использую ключ -r для поддержки регулярных выражений, конкретно
.*?
. Можно ещё усложнить и выполнить замену кода между каких-то строк. Для усложнения возьмём какой-нибудь XML:
<username><![CDATA[user01]]></username>
<password><![CDATA[password01]]></password>
<dbname><![CDATA[database]]></dbname>
Заменим user01 на user02
# sed -r 's/(<username>.+)user01(.+<\/username>)/\1user02\2/' test.xml
Тут важны круглые скобки и \1 и \2. Мы в первой части выражения запомнили текст в круглых скобках, а во второй части его использовали — сначала первую скобку, потом вторую.
Это были примеры для одиночных файлов, а теперь добавляем сюда find и используем sed на любом наборе файлов, который найдёт find.
# find /var/www/ -type f -name \*.php -exec \
sed -i -r 's/<script>function aeaab19d\(a\).*?<\/script>//' {} \;
Добавляем к sed ключ -i для того, чтобы он сразу изменял файл. Кстати, для find наиболее популярные примеры можете посмотреть через тэг #find.
Очень аккуратно выполняйте массовые действия. Сначала всё отладьте на тестовых файлах. Потом сделайте бэкап исходных файлов. И только потом выполняйте массовые изменения. И будьте готовы быстро всё откатить обратно.
Примеры рекомендую записать. Если надо быстро что-то сделать, то сходу правильно регулярку вы так просто не наберёте. К тому же в таком использовании есть свои нюансы. К примеру, я так и не смог победить команду sed, которая удаляет весь код <script>, если внутри есть переход на новую строку. Вроде бы легко найти, как заставить
.
в регулярках учитывать и переход на новую строку, но на практике у меня это не получилось сделать. Я не понял, как правильно составить выражение для sed. Не забывайте про сервисы, которые помогают отлаживать регулярки. Собрал их в отдельной заметке.
#linux #bash #script
И вновь возвращаемся к рубрике с современными утилитами, которые можно использовать в качестве альтернативы более старым, потому что они уже поселились в официальных репозиториях. Речь пойдёт об уже упоминаемой мной несколько лет назад программе fd, которую можно использовать вместо find. Она приехала в стабильные репы:
Короткое название fd использовать не получится, потому что оно уже занято. Придётся использовать
В чём особенность fd? Авторы называют её более быстрой и дружелюбной к пользователю программой, по сравнению с find. В принципе, с этим не поспоришь. Ключи и синтаксис find нельзя назвать дружелюбным. Я по памяти могу только какие-то самые простые вещи делать. Всё остальное ищу в своих шпаргалках. Не понимаю, как подобные конструкции можно запоминать, если каждый день с ними не работаешь.
Ищем файл syslog в текущей директории:
Ищем файл в конкретной директории:
Ищем файл по всему серверу:
Вот то же самое, только с find:
По умолчанию find найдёт только полное совпадение по имени. Файл должен будет точно называться syslog, чтобы попасть в результаты поиска. Fd в примере отобразит все файлы, где встречается слово syslog. Это и интуитивнее, и проще для запоминания. Хотя этот момент субъективен. С find такой же поиск будет выглядеть вот так:
Отличия от find, которые отмечают авторы:
◽️Более скоростной поиск за счёт параллельных запросов. Приводят свои измерения, где это подтверждается.
◽️Подсветка результатов поиска.
◽️По умолчанию поиск ведётся без учёта регистра.
◽️Итоговые команды в среднем на 50% короче, чем то же самое получается с find.
⚠️ Отдельный ключ
Несколько простых примеров для наглядности. Поиск файлов с определённым расширением:
Найти все архивы и распаковать их:
Найти файлы с расширением gz и удалить одной командой rm:
Вызванный без аргументов в директории, fd рекурсивно выведет все файлы в ней:
Это примерно то же самое, что с find:
Только у fd вывод нагляднее, так как уже отсортирован по именам. А find всё вперемешку выводит. Ему надо sort добавлять, чтобы то же самое получилось:
Fd умеет результаты выводить не по одному значению на каждую строку, а непрерывной строкой с разделением с помощью так называемого NULL character. Все символы воспринимаются буквально, не являются специальными, их не надо экранировать. Актуально, когда результаты поиска имеют пробелы, кавычки, косые черты и т.д. Например, xargs умеет принимать на вход значения, разделённые NULL character. У него для этого отдельный ключ
Покажу на примере, как это работает:
Xargs не смог обработать файл с пробелом. А вот так смог:
И ничего экранировать не пришлось. Полезная возможность, стоит запомнить. Find то же самое делает через ключ
В заключении резюмирую, что fd не заменяет find, не претендует на это и не обеспечивает такую же функциональность, как у последнего. Он предоставляет обоснованные настройки по умолчанию для наиболее массовых применений поиска пользователями.
#terminal #linux #find
# apt install fd-find
Короткое название fd использовать не получится, потому что оно уже занято. Придётся использовать
fdfind
. Под Windows она, кстати, тоже есть:> winget install sharkdp.fd
В чём особенность fd? Авторы называют её более быстрой и дружелюбной к пользователю программой, по сравнению с find. В принципе, с этим не поспоришь. Ключи и синтаксис find нельзя назвать дружелюбным. Я по памяти могу только какие-то самые простые вещи делать. Всё остальное ищу в своих шпаргалках. Не понимаю, как подобные конструкции можно запоминать, если каждый день с ними не работаешь.
Ищем файл syslog в текущей директории:
# fdfind syslog
Ищем файл в конкретной директории:
# fdfind syslog /var/log
Ищем файл по всему серверу:
# fdfind syslog /
Вот то же самое, только с find:
# find . -name syslog
# find /var/log -name syslog
# find / -name syslog
По умолчанию find найдёт только полное совпадение по имени. Файл должен будет точно называться syslog, чтобы попасть в результаты поиска. Fd в примере отобразит все файлы, где встречается слово syslog. Это и интуитивнее, и проще для запоминания. Хотя этот момент субъективен. С find такой же поиск будет выглядеть вот так:
# find / -name '*syslog*'
Отличия от find, которые отмечают авторы:
◽️Более скоростной поиск за счёт параллельных запросов. Приводят свои измерения, где это подтверждается.
◽️Подсветка результатов поиска.
◽️По умолчанию поиск ведётся без учёта регистра.
◽️Итоговые команды в среднем на 50% короче, чем то же самое получается с find.
⚠️ Отдельный ключ
--exec-batch
или -X
в дополнение к традиционному --exec
. Разница в том, что --exec
выполняется параллельно для каждого результата поиска, а с помощью --exec-batch
можно во внешнюю команду передать весь результат поиска. Это важный момент, который где-то может сильно выручить и упростить задачу. Даже если вам сейчас это не надо, запомните, что с fd так можно. Где-то в скриптах это может пригодиться.Несколько простых примеров для наглядности. Поиск файлов с определённым расширением:
# fdfind -e php -p /var/www
Найти все архивы и распаковать их:
# fdfind -e zip -p /home/user -x unzip
Найти файлы с расширением gz и удалить одной командой rm:
# fdfind -e gz -p /var/log -X rm
Вызванный без аргументов в директории, fd рекурсивно выведет все файлы в ней:
# cd /var/log
# fdfind
Это примерно то же самое, что с find:
# find -name '*'
Только у fd вывод нагляднее, так как уже отсортирован по именам. А find всё вперемешку выводит. Ему надо sort добавлять, чтобы то же самое получилось:
# find -name '*' | sort
Fd умеет результаты выводить не по одному значению на каждую строку, а непрерывной строкой с разделением с помощью так называемого NULL character. Все символы воспринимаются буквально, не являются специальными, их не надо экранировать. Актуально, когда результаты поиска имеют пробелы, кавычки, косые черты и т.д. Например, xargs умеет принимать на вход значения, разделённые NULL character. У него для этого отдельный ключ
-0, --null
. Покажу на примере, как это работает:
# touch 'test test.log'
# fdfind -e log
...
test test.log
# fdfind -e log | xargs wc -l
...
wc: test: No such file or directory
wc: test.log: No such file or directory
Xargs не смог обработать файл с пробелом. А вот так смог:
# fdfind -0 -e log | xargs -0 wc -l
...
0 ./test test.log
И ничего экранировать не пришлось. Полезная возможность, стоит запомнить. Find то же самое делает через ключ
-print0
:# find . -name '*.log' -print0 | xargs -0 wc -l
В заключении резюмирую, что fd не заменяет find, не претендует на это и не обеспечивает такую же функциональность, как у последнего. Он предоставляет обоснованные настройки по умолчанию для наиболее массовых применений поиска пользователями.
#terminal #linux #find