2015-03-26 2 views
6

У меня есть команда, которая пытается генерировать UUID, для файлов:Может ли xargs выполнить команду subshell для каждого аргумента?

find -printf "%P\n"|sort|xargs -L 1 echo $(uuid) 

Но в результате xargs только выполнение $(uuid) подоболочка раз:

8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1 
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2 
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3 

Есть один вкладыш (т.е. не функция), чтобы получить xargs для выполнения команды подоболочки на каждом входе?

+1

@TomFenech: '-n 1' фактически расколот любой пробел, будь то линейный интерьер или нет, поэтому команда будет разбиваться на пути со встроенным пробелом; '-L 1' приближается к намерению, поскольку он выполняет линейную обработку, но разделение слов по-прежнему применяется к каждой строке, так что потенциально _multiple_ аргументы передаются в' echo' на строку ввода (что может или может не вызывать проблем). Надежный подход заключается в использовании '-I', как в принятом ответе. – mklement0

ответ

10

Это потому, что $(uuid) расширяется в текущей оболочке. Вы можете явно вызвать оболочку:

find -printf "%P\n"| sort | xargs -I '{}' bash -c 'echo $(uuid) {}' 

Btw, я хотел бы использовать следующую команду:

find -exec bash -c 'echo "$(uuid) ${1#./}"' -- '{}' \; 

без xargs.

+2

Красиво сделано; но не только '-n 1' является излишним, потому что' -I' подразумевает построчную обработку, '-n 1' будет фактически разделяться пробелом _any_, будь то интерьером или нет. В то время как '-L 1' выполняет поэтапную обработку, словосочетание по-прежнему применяется к каждой строке, тогда как' -I' обрабатывает всю строку как аргумент _single_. – mklement0

+1

@ mklement0 Спасибо, что указали это! Отредактировано, что – hek2mgl

2

С для цикла:

for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i"; done 

Редактировать: еще один способ сделать это:

find -printf "%P\0" -exec uuid -v 4 \; | sort | awk -F'\0' '{ print $2 " " $1}' 

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

+0

Это также работает и представляет собой немного более легкую версию для чтения, а также отсутствие накладных расходов на новый баш для каждого аргумента. Если бы я мог разделить кредит, я бы это сделал. Благодарю. – adelphus

+0

Использование цикла оболочки в этом примере является хорошей идеей по причинам производительности, но лучше использовать цикл while, потому что 'for' будет разбиваться на имена файлов со встроенными пространствами, например, см. Http: //mywiki.wooledge .org/DontReadLinesWithFor – mklement0

+1

@ mklement0 это очень верно; во всяком случае, я решил, что это лучше отбрасывает цикл –

2

hek2mgl's answer хорошо объясняет проблему, и его решение хорошо работает; этот ответ выглядит как производительность.

Принимаемый ответ медленный, потому что он создает процесс bash для каждой строки ввода.

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

Следующее альтернативное решение использует while цикла для обработки входных линий, а также, на моей машине, составляет около в два раза быстрее в качестве xargs раствора.

find . -printf "%P\n" | sort | while IFS= read -r f; do echo "$(uuid) $f"; done 
  • Обратите внимание на использование while, а не for, потому что for не может решительно разобрать вывод команды (короче говоря: имена файлов со встроенным пробельных разорвет команду - см http://mywiki.wooledge.org/DontReadLinesWithFor).

Если вы беспокоитесь о именах файлов с вложенными символами новой строки (очень редко) и использовать GNU утилиты, вы можете использовать NUL байт в качестве разделителей:

find . -printf "%P\0" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done 

Update: самый быстрый подход не должен использовать петлю оболочки вообще, о чем свидетельствует ᴳᵁᴵᴰᴼ's clever answer. См. Ниже портативную версию его ответа.


примечание Совместимость:

find Командование OP подразумевает использование GNUfind (Linux), и использует функции (-printf), которые не могут работать на других платформах.

Вот портативная версия ᴳᵁᴵᴰᴼ's answer, который использует только POSIX-совместимые функции findawk).
Обратите внимание, что uuid не является утилитой POSIX; так как Linux и BSD-подобные системы (включая OSX) имеют uuidgen полезность, команда использует, что вместо того, чтобы:

find . -exec printf '%s\t' {} \; -exec uuidgen \; | 
    awk -F '\t' '{ sub(/.+\//,"", $1); print $2, $1 }' | sort -k2