Изменить текст в аргументе для xargs (или GNU Parallel)

У меня есть программа, которую я могу запустить двумя способами: в одностороннем или парном режиме. Вот синтаксис:

program <output-directory-name> <input1> [input2]

Где требуется выходной каталог и хотя бы один вход. Если бы я хотел запустить это для трех файлов, скажем, образцов A, B и C, я бы использовал что-то вроде find с xargs или parallel:

user@host:~/single$ ls
sampleA.txt  sampleB.txt  sampleC.txt

user@host:~/single$ find . -name "sample*" | xargs -i echo program {}-out {}
program ./sampleA.txt-out ./sampleA.txt
program ./sampleB.txt-out ./sampleB.txt
program ./sampleC.txt-out ./sampleC.txt

user@host:~/single$ find . -name "sample*" | parallel --dry-run program {}-out {}
program ./sampleA.txt-out ./sampleA.txt
program ./sampleB.txt-out ./sampleB.txt
program ./sampleC.txt-out ./sampleC.txt

Но когда я хочу запустить программу в «парном» режиме, мне нужно дать ей два входа. Это связанные файлы, но их нельзя просто объединить — вы должны запустить программу с обоими в качестве входных данных. Файлы имеют разумные имена, например, sampleA_1.txt и sampleA_2.txt.

Я хочу иметь возможность легко создать это в командной строке с чем-то вроде xargs (или предпочтительно параллельно):

user@host:~/paired$ ls
sampleA_1.txt  sampleB_1.txt  sampleC_1.txt
sampleA_2.txt  sampleB_2.txt  sampleC_2.txt

user@host:~/paired$ find . -name "sample*_1.txt" | sed/awk? | parallel ?
program ./sampleA-out ./sampleA_1.txt ./sampleA_2.txt
program ./sampleB-out ./sampleB_1.txt ./sampleB_2.txt
program ./sampleC-out ./sampleC_1.txt ./sampleC_2.txt

В идеале команда должна удалить _1.txt, чтобы создать имя выходного каталога (sampleA-out и т. д.), но мне действительно нужно иметь возможность взять этот аргумент и изменить _1 на _2 для второго ввода.

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

Заранее спасибо.


person Stephen Turner    schedule 13.03.2012    source источник


Ответы (4)


Я сделал это на Perl с помощью быстрой замены регулярных выражений. Но я хотел бы иметь возможность сделать это с помощью быстрого однострочника.

В Perl тоже есть однострочники, как и в sed и awk. Ты можешь написать:

find . -name "sample*_1.txt" | perl -pe 's/_1\.txt$//' | parallel program {}-out {}_1.txt {}_2.txt

(Флаг -e означает, что «следующим аргументом является текст программы»; флаг -p означает, что «программа должна выполняться в цикле; для каждой строки ввода установите $_ в эту строку, затем запустите программу, затем напечатайте $_». )

person ruakh    schedule 13.03.2012
comment
Мне нравится этот ответ больше всего, потому что он взаимозаменяем либо с xargs, либо с parallel. Спасибо за подсказку и за объяснение того, что делают -p и -e. Похоже, я могу сделать то же самое с sed 's/_1\.txt//g'. Теперь это кажется очевидным. Спасибо. - person Stephen Turner; 13.03.2012

С sed и xargs вы можете сделать что-то вроде этого:

find . -name "sample*_1.txt" | sed -n 's/_1\..*$//;h;s/$/_out/p;g;s/$/_1.txt/p;g;s/$/_2.txt/p' | xargs -L 3 echo program

То есть: sed создает три аргумента, а xargs -L 3 составляет командные строки с тремя аргументами.

person bmk    schedule 13.03.2012

Предполагая, что у вас всегда есть ровно 2 файла в вашем каталоге для каждой пары, и предполагая, что они правильно отсортированы по find (это вы можете убедиться, передав результаты от find до sort), возможно, xargs -l 2 выполнит эту работу. Это говорит xargs размещать 2 последовательных входящих параметра в каждой выполняемой командной строке.

person Michał Kosmulski    schedule 13.03.2012

Более короткая версия:

parallel --xapply program {1.}.out {1} {2} :::: <(ls *_1.txt) <(ls *_2.txt)

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

person Ole Tange    schedule 15.03.2012