Pull to refresh

Comments 44

Перешёл с xargs на GNU Parallel — сильно быстрее во многих случаях, особенно на сложных операциях над файлами.
> Как видим, файл, в имени которого имеется пробел, не был удалён.

~/Tmp$> touch "new file.sh" new_file.sh
~/Tmp$> find . -name "new*.sh"
./new file.sh
./new_file.sh
~/Tmp$> find . -name "new*.sh" -exec rm -f '{}' \;
~/Tmp$> find . -name "new*.sh"
~/Tmp$>
А вы ниже прочитайте на абзац.
Ну а так, то у find есть ключ -delete.
И зачем тут тогда вообще нужен xargs? :)
У автора спросите )) Думаю, что просто приведено как пример.
-delete есть в FreeBSD- и GNU-версии find, наверное где-то еще.
Но кроме GNU существует и масса других реализаций find, без -delete. В SunOS например этого нет.
В SunOS есть -exec (он, емнип, везде есть). А с ним через rm все прекрасно работает.
exec будет вызываться для каждого файла отдельно
а xargs может скормить группу аргументов тому же rm. это работает еще «прекраснее» :)
Я не знаю, где вы это прочитали, но УМВР.
 $ touch "test test.sh" "test.sh"
 $ ls -l
total 0
-rw------- 1 user group 0 Jan 21 01:16 test.sh
-rw------- 1 user group 0 Jan 21 01:16 test test.sh
 $ find . -name "t*.sh" -exec rm -vf '{}' \; 
removed ‘./test test.sh’
removed ‘./test.sh’
Картинка — скрин из статьи (см. мой коммент выше по ветке).
вашу find конструкцию можно существенно ускорить заменив всего лишь один символ:
find . -name "new*.sh" -exec rm -f '{}' \+
оно отработает существенно быстрее, т.к. не будет на каждый файл отдельно вызывать rm. на паре-тройке файлов это незаметно, но когда счёт идёт на даже на сотни это уже видно невооруженным глазом. и \+ даже не гнутое расширение, а posix. и впилили его уже не один год назад. да (:
О, а вот за это — спасибо, не знал о такой штуке (да, я иногда невнимательно читаю длинные мануалы :().
А насколько быстро отработает эта команда на списке из 27 миллионов файлов? Точно ли, если использовать \+, rm не выдаст заветное «too long list of arguments»?
Имхо, с find лучше всего использовать ключ -delete, который просто возьмет и удалит все найденные файлы вне зависимости от их количества.
«too long list of arguments» вроде же оболочка выдаёт, до rm дело не доходит, а если бы дошло, то, он бы не знал о слишком длинном списке. Вообще xargs умный, если длина команды будет близка к максимальной, то команда вызовется, а накопленный список очистится. "+" не значит, что вызов будет один, он значит, что команда может принимать много аргументов и xargs должен стараться передать как можно больше за раз.
> до rm дело не доходит, а если бы дошло, то, он бы не знал о слишком длинном списке

У меня был случай, когда rm -rf dir/* ругнулось, а rm -rf dir — отработало.
Всё верно. dir/* раскрывает список файлов, поэтому оболочка ругается на огромный список. dir ничего не раскрывает, поэтому ругаться не на что.
А каков максимальный размер списка? Сходу ничего не нагуглилось.
«sysconf(_SC_ARG_MAX)» >= 4096. На современных Linux-based системах обычно 2 MiB.
в случае с удалением пример немного специфический. вам просто повезло, что у find'a есть -delete. а если вам всё это счастье скопировать нужно? -copy у find'а нет, поэтому либо xargs, либо
find . -options -exec cp -t target {} \+
А для cp нельзя прикрутить, чтобы найденные файлы в указанный каталог перекинуть одним вызовом?
+ обязательно после {} должен идти?
Можно. Если почитать man cp, то можно наткнуться на любопытную опцию -t:
       -t, --target-directory=DIRECTORY
              copy all SOURCE arguments into DIRECTORY

Т.е. выглядит оно примерно так:
find . -type f -exec cp -t TARGET {} \+

Аналогичный трюк можно провернуть с mv.

upd. Что-то я сразу не заметил, ведь именно так и написано в моём предыдущем комментарии. Т.е. оно всё что найдёт и перекинет в указанную директорию. Ну или я вопрос неправильно понял.
Я буду читать все комментарии!
Спасибо :)
Переименование файлов

$ ls | sed -e «p;s/.txt$/.sql/» | xargs -n2 fmv


А можно еще проще:
rename 's/\.txt$/.sql' *.txt


Команда rename широко распространена, в debian/ubuntu стоит по умолчанию
Замечу, что есть разные rename. В генте правильная rename это dev-perl/rename и вызывается как perl-rename
У find есть ключик -delete, работает быстрее чем с использованием xargs и exec.
Смотреть:
Тут(GNU.org)
(find /proc/ -name exe -ls 2>/dev/null|awk '{print $13}'|tee /dev/stderr| xargs -n 32 -P 4 ldd 2>/dev/null|grep /|awk '{print $3}') 2>&1|sort -u|xargs -n 1 -P 16 readlink -f|xargs -n 32 -P 4 du -b|awk '{sum+=$1}END{print sum}'

Функциональное программирование в шелле на примере xargs: habrahabr.ru/post/153785/
Некоторые примеры мягко говоря не совсем подходящие:
* find умеет делать -delete сам
* для упаковки файлов можно использовать обычный пайп: find. -type f | tar czf arc.tgz
* $ cut -d: -f1 < /etc/passwd | sort | xargs echo < — зачем этот echo? sort и так отлично возвращает вывод в stdout

Как-то так.
Сам я чаще всего использую exec внутри find, вместо xargs. В остальных случаях циклы покрывают 99% потребностей. Для работы с именами в которых есть пробелы проще всего экранировать «маркер». Например так:
bash-4.2# ls
a b c d
bash-4.2# touch "1 2 3 4"
bash-4.2# ls | xargs -I{} mv "{}" "{}".sh
bash-4.2# ls
1 2 3 4.sh  a b c d.sh
bash-4.2#
Всегда думал — «Как Тираны обходились такими маленькими ручками?»
Теперь я все понял! Спасибо :)

ЗЫ *nix советы тоже интересны и поучительны, но картинка… Это нечто.
А давайте усугубим пример с find… | xargs rm. Для этого сначала почти бесправный пользователь bob делает:
mkdir "~bob/user-controlled/space /etc"
touch "~bob/user-controlled/space /etc/passwd"

Потом всевластный root в скрипте по крону пишет:
find ~bob -name "*" | xargs rm -f

И в итоге некоторые файлы не то, чтобы совсем не удаляются, но удаляются не те, что нужно.

Место для хождения по граблям в статье выделено совершенно правильно, да и аргументом xargs может быть не только rm.
# самое простое
find -name ... -delete
# удалять файлы в 4 потока по 8 штук за раз
find -type f -name ... -print0 | xargs -0 -P$(nproc) -n8 rm

Насчёт параллельного запуска да и самого xargs можно было и побольше примеров придумать:

# нехитрый способ конвертировать все mp3 в ogg
find -name '*.mp3' -print0 | xargs -0 -n1 -P$(nproc) sh -c 'ffmpeg -v quiet -y -i "$0" -vn -codec vorbis -aq 2 "${0%mp3}ogg"'
# поиск несмердженных веток Git
git branch -r --no-merged | grep -v HEAD | xargs -L1 git --no-pager log --pretty=tformat:'%Cgreen%d%Creset - %h by %an (%Cblue%ar%Creset)' -1
# wget в 8 потоков (список ссылок для загрузки лежит в файле .list)
< .list xargs -I{} -P8 sh -c 'echo {}; wget -q {}'
# универсальный скрипт - функция _worker выполнится для каждого подкаталога в текущем каталоге (в несколько потоков, разумеется)
_worker() {
    cd "$1"
    # ...что-то делаем
}
export -f _worker
find . -maxdepth 1 -mindepth 1 -type d | xargs -P$(nproc) -I{} bash -c '_worker {}'

find, xargs и nproc выручают меня постоянно, особенно когда речь идёт о серверах с кучей ядер. В статье как-то скудновато описано.
мне как-то нужно было скачать первую страницу всех сайтов рунета.

сначала через fping определи все домены которые резолвятся и сохранил их в отдельный файл, а потом

cat unqiq.domains.txt | xargs -P 800 -I _URL_ ./crawler.sh _URL_


где crawler.sh это просто wget c нужными настройками (таймаутами). За 6 часов все скачалось.
нужно было скачать первую страницу всех сайтов рунета

Так вот как ты выглядишь, движок Яндекса.

Пробовал я как-то запустить резолвер на несколько тыс. доменов в многопоточном режиме — роутер повесился намертво.
Я может чего не понял, xargs иногда путает, но вот это:

«С помощью xargs можно также добавлять к дополнительные элементы к именам файлов (например, дату):

$ ls | xargs -I FILE mv {} <… >-{}»

— не будет работать. Тогда уж так:
$ ls | xargs -I FILE mv FILE <… >-FILE

Ну и в других местах…
какую задачу выполняет xargs в комманде?

tr -dc A-Za-z0-9_ < /dev/urandom | head -c 10 | xargs


PS: Ждем след статью на тему «как использовать grep и echo»
tr -dc A-Za-z0-9_ < /dev/urandom | head -c 10 | grep ''
tr -dc A-Za-z0-9_ < /dev/urandom | head -c 10 && echo


По iptables — не стоит применять длинные линейные списки там, где можно применить ipset.
UFO just landed and posted this here
Sign up to leave a comment.