"найти" и "ls" с GNU параллельно - PullRequest
       20

"найти" и "ls" с GNU параллельно

10 голосов
/ 30 сентября 2011

Я пытаюсь использовать GNU parallel для публикации большого количества файлов на веб-сервере. В моем каталоге у меня есть несколько файлов:

file1.xml
file2.xml

и у меня есть скрипт оболочки, который выглядит следующим образом:

#! /usr/bin/env bash

CMD="curl -X POST -d@$1 http://server/path"

eval $CMD

В сценарии есть еще кое-что, но это был самый простой пример. Я попытался выполнить следующую команду:

ls | parallel -j2 script.sh {}

Это то, что страницы GNU parallel показывают как "нормальный" способ работы с файлами в каталоге. Кажется, это передает имя файла в мой скрипт, но curl жалуется, что не может загрузить переданный файл данных. Однако, если я это сделаю:

find . -name '*.xml' | parallel -j2 script.sh {}

работает нормально. Есть ли разница между тем, как ls и find передают аргументы моему сценарию? Или мне нужно сделать что-то дополнительное в этом скрипте?

Ответы [ 4 ]

6 голосов
/ 01 октября 2011

GNU parallel является вариантом xargs. Они оба имеют очень похожие интерфейсы, и если вам нужна помощь по parallel, вам может повезти, если вы найдете информацию о xargs.

Это, как говорится, способ, которым они оба работают, довольно прост. С их поведением по умолчанию обе программы считывают ввод из STDIN, а затем разбивают ввод на токены на основе пробелов. Каждый из этих токенов затем передается в предоставленную программу в качестве аргумента. По умолчанию xargs передает программе как можно больше токенов, а затем запускает новый процесс при достижении предела. Я не уверен, как работает по умолчанию для параллельного.

Вот пример:

> echo "foo    bar \
  baz" | xargs echo
foo bar baz

Существуют некоторые проблемы с поведением по умолчанию, поэтому часто можно увидеть несколько вариантов.

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

> echo "Success!" > bad\ filename
> find . "bad\ filename" -print0 | xargs -0 cat
Success!

Опция -print0 указывает find разделять файлы символом NULL вместо пробела.
Опция -0 указывает xargs использовать символ NULL для маркировки каждого аргумента.

Обратите внимание, что parallel немного лучше, чем xargs в том смысле, что его поведение по умолчанию - это токенизация вокруг только новых строк, поэтому нет необходимости изменять поведение по умолчанию.

Другая распространенная проблема заключается в том, что вы можете контролировать, как аргументы передаются в xargs или parallel. Если вам нужно конкретное размещение аргументов, передаваемых программе, вы можете использовать {}, чтобы указать, где должен располагаться аргумент.

> mkdir new_dir
> find -name *.xml | xargs mv {} new_dir

Это переместит все файлы в текущем каталоге и подкаталогах в каталог new_dir. Это на самом деле распадается на следующее:

> find -name *.xml | xargs echo mv {} new_dir
> mv foo.xml new_dir
> mv bar.xml new_dir
> mv baz.xml new_dir

Итак, учитывая, как работают xargs и parallel, вы должны надеяться увидеть проблему с вашей командой. find . -name '*.xml' создаст список файлов xml, которые будут переданы программе script.sh.

> find . -name '*.xml' | parallel -j2 echo script.sh {}
> script.sh foo.xml
> script.sh bar.xml
> script.sh baz.xml

Однако ls | parallel -j2 script.sh {} сгенерирует список ВСЕХ файлов в текущем каталоге для передачи в программу script.sh.

> ls | parallel -j2 echo script.sh {}
> script.sh some_directory
> script.sh some_file
> script.sh foo.xml
> ...

Более правильный вариант для версии ls будет выглядеть следующим образом:

> ls *.xml | parallel -j2 script.sh {}

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

> find -maxdepth 1 -name '*.xml'

Это будет искать только текущий каталог.

3 голосов
/ 02 октября 2011

Поскольку он работает с find, вы, вероятно, захотите посмотреть, какая команда GNU Parallel работает (с помощью -v или --dryrun), а затем попытаться запустить ошибочные команды вручную.

2 голосов
/ 30 сентября 2011

Я не использовал parallel, но есть различия между ls & find . -name '*.xml'. ls будет перечислять все файлы и каталоги, где find . -name '*.xml' будет перечислять только файлы (и каталоги), которые заканчиваются на .xml .
По предложению Пола Рубеля, просто напечатайте значение $ 1 в вашем скрипте, чтобы проверить это. Кроме того, вы можете рассмотреть возможность фильтрации входных файлов только в find с параметром -type f.
Надеюсь, это поможет!

1 голос
/ 30 сентября 2011

Ухоженная.

Я никогда раньше не использовал параллель.Похоже, что их два.Одним из них является Gnu Parrallel, а тот, который был установлен в моей системе, в качестве автора на страницах man указан Tollef Fog Heen.

Как упоминал Пол, вы должны использовать set -x

Кроме того, парадигма, которую вы упомянули выше, похоже, не работает на моей параллели, скорее, я должен сделать следующее:

$ cat ../script.sh
+ cat ../script.sh
#!/bin/bash
echo $@
$ parallel -ij2 ../script.sh {} -- $(find -name '*.xml')
++ find -name '*.xml'
+ parallel -ij2 ../script.sh '{}' -- ./b.xml ./c.xml ./a.xml ./d.xml ./e.xml
./c.xml
./b.xml
./d.xml
./a.xml
./e.xml
$ parallel -ij2 ../script.sh {} -- $(ls *.xml)
++ ls --color=auto a.xml b.xml c.xml d.xml e.xml
+ parallel -ij2 ../script.sh '{}' -- a.xml b.xml c.xml d.xml e.xml
b.xml
a.xml
d.xml
c.xml
e.xml

find предоставляет другой вход, он предопределяет относительный путь кназвание.Может быть, это то, что портит ваш сценарий?

...