xargs с перенаправлением stdin / stdout

21

Я хотел бы запустить:

./a.out < x.dat > x.ans

для каждого * .dat файла в каталоге A .

Конечно, это может быть сделано скриптом bash / python / whatSever, но мне нравится писать сексуальные однострочные. Все, чего я мог достичь, это (все еще без какого-либо стандартного выхода)

ls A/*.dat | xargs -I file -a file ./a.out

Но -a в xargs не понимает файл-замены replace-str.

Спасибо за помощь.

Николай Вяххи
источник
В дополнение к другим хорошим ответам здесь вы можете использовать опцию -a GNU xargs.
Джеймс Янгман

Ответы:

30

Прежде всего, не используйте lsвывод как список файлов . Используйте расширение оболочки или find. Ниже приведены возможные последствия неправильного использования ls + xargs и пример правильного xargsиспользования.

1. Простой способ: для цикла

Если вы хотите обрабатывать только файлы, расположенные ниже A/, то простого forцикла должно быть достаточно:

for file in A/*.dat; do ./a.out < "$file" > "${file%.dat}.ans"; done

2. pre1 Почему бы и нет   ls | xargs ?

Вот пример того, как плохие вещи могут обернуться, если вы используете lsс xargsдля работы. Рассмотрим следующий сценарий:

  • во-первых, давайте создадим несколько пустых файлов:

    $ touch A/mypreciousfile.dat\ with\ junk\ at\ the\ end.dat
    $ touch A/mypreciousfile.dat
    $ touch A/mypreciousfile.dat.ans
    
  • посмотрите файлы и чтобы они ничего не содержали:

    $ ls -1 A/
    mypreciousfile.dat
    mypreciousfile.dat with junk at the end.dat
    mypreciousfile.dat.ans
    
    $ cat A/*
    
  • запустить магическую команду, используя xargs:

    $ ls A/*.dat | xargs -I file sh -c "echo TRICKED > file.ans"
    
  • результат:

    $ cat A/mypreciousfile.dat
    TRICKED with junk at the end.dat.ans
    
    $ cat A/mypreciousfile.dat.ans
    TRICKED
    

Так что вам только что удалось перезаписать и то mypreciousfile.datи другое mypreciousfile.dat.ans. Если бы в этих файлах было какое-либо содержимое, оно было бы стерто.


2. Использование   xargs : правильно с  find 

Если вы хотите настаивать на использовании xargs, используйте -0(имена, оканчивающиеся нулем):

find A/ -name "*.dat" -type f -print0 | xargs -0 -I file sh -c './a.out < "file" > "file.ans"'

Обратите внимание на две вещи:

  1. таким образом, вы будете создавать файлы с .dat.ansокончанием;
  2. это сломается, если какое-либо имя файла содержит знак кавычки ( ").

Обе проблемы могут быть решены с помощью различных способов вызова оболочки:

find A/ -name "*.dat" -type f -print0 | xargs -0 -L 1 bash -c './a.out < "$0" > "${0%dat}ans"'

3. Все сделано в течение find ... -exec

 find A/ -name "*.dat" -type f -exec sh -c './a.out < "{}" > "{}.ans"' \;

Это, опять же, производит .dat.ansфайлы и сломается, если имена файлов содержат ". Для этого используйте bashи измените способ его вызова:

 find A/ -name "*.dat" -type f -exec bash -c './a.out < "$0" > "${0%dat}ans"' {} \;
rozcietrzewiacz
источник
+1 за упоминание не разбирать вывод ls.
Рахму
2
Вариант 2 прерывается, когда имена файлов содержат ".
thiton
2
Очень хороший момент, спасибо! Я обновлю соответственно.
rozcietrzewiacz
Я просто хочу упомянуть, что, если zshиспользуется в качестве оболочки (а SH_WORD_SPLIT не установлен), все неприятные особые случаи (пробелы, "в имени файла и т. Д.) Не должны рассматриваться. Тривиальный for file in A/*.dat; do ./a.out < $file > ${file%.dat}.ans ; doneработает во всех случаях.
jofel
-1 потому что я пытаюсь понять, как сделать xargs с помощью stdin, и мой вопрос не имеет ничего общего с файлами или find.
Мердад
2

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

$ for i in $(find A/ -name \*.dat); do ./a.out < ${i} > ${i%.dat}.ans; done

rahmu
источник
Это не сработает. Он будет пытаться работать с такими вещами, как somefile.dat.datи перенаправить весь вывод в один файл.
rozcietrzewiacz
Вы правы. Я отредактировал решение, чтобы исправить это.
Рахму
ОК - Почти хорошо :) Просто somefile.dat.ansвывод материала будет выглядеть не так хорошо.
rozcietrzewiacz
1
Под редакцией! Я не знал о «%». Это работает как шарм, спасибо за совет.
Рахму
1
Добавление -type fileбыло бы хорошо (не может < directory), и это делает фею необычного имени файла печальной.
Мат
2

Для простых шаблонов подходит цикл for:

for file in A/*.dat; do
    ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Для более сложных случаев, когда вам нужна другая утилита для вывода списка файлов (zsh или bash 4 имеют достаточно мощные шаблоны, которые вам редко нужно находить, но если вы хотите остаться в оболочке POSIX или использовать быструю оболочку, такую ​​как dash, вам понадобится найти что-нибудь нетривиально), хотя чтение наиболее уместно:

find A -name '*.dat' -print | while IFS= read -r file; do
   ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Это будет обрабатывать пробелы, потому что чтение (по умолчанию) ориентировано на строки. Он не будет обрабатывать символы новой строки и не будет обрабатывать обратную косую черту, потому что по умолчанию он интерпретирует escape-последовательности (что фактически позволяет передавать символы новой строки, но команда find не может сгенерировать этот формат). Многие оболочки имеют -0опцию для чтения, поэтому в них вы можете обрабатывать все символы, но, к сожалению, это не POSIX.

Ян Худек
источник
2

Используйте GNU Parallel:

parallel ./a.out "<{} >{.}.ans" ::: A/*.dat

Дополнительный бонус: обработка выполняется параллельно.

Посмотрите вступительные видео, чтобы узнать больше: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Оле Танге
источник
1

Я думаю, что вам нужно, по крайней мере, вызов оболочки в xargs:

ls A/*.dat | xargs -I file sh -c "./a.out < file > file.ans"

Изменить: Следует отметить, что этот подход не работает, когда имена файлов содержат пробелы. Не могу работать Даже если бы вы использовали команды find -0 и xargs -0, чтобы xargs правильно понимал пробелы, вызов оболочки -c обрушился бы на них. Однако OP явно попросил решение xargs, и это лучшее решение xargs, которое я придумал. Если пробелы в именах файлов могут быть проблемой, используйте find -exec или цикл оболочки.

thiton
источник
@rozcietrzewiacz: В общем, конечно, но я предполагаю, что кто-то пытается сделать xargs voodoo, знает это. Поскольку эти имена файлов подвергаются другому расширению оболочки, они должны быть в любом случае хорошими.
thiton
1
Вы не правы по поводу расширения оболочки. lsвыход можно такие вещи , как пространства , не избежать и что это проблема.
rozcietrzewiacz
@rozcietrzewiacz: Да, я понимаю эту проблему. Теперь предположим, что вы должным образом экранировали эти пробелы и поместили их в xargs: они заменяются на строку sh's -c, sh токенизирует строку -c, и все ломается.
thiton
Да. Теперь вы видите, что анализ lsне годится. Но xargs можно безопасно использовать с find - см. Предложение № 2 в моем ответе.
rozcietrzewiacz
1

Там нет необходимости усложнять это. Вы можете сделать это с помощью forцикла:

for file in A/*.dat; do
  ./a.out < "$file" >"${file%.dat}.ans"
done

${file%.dat}.ansбит будет удалить .datсуффикс имени файла из файла в $fileи вместо того, чтобы добавить .ansв конец.

Кусалананда
источник