具有stdin / stdout重定向的xargs


21

我想跑步:

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

对于目录A中的每个* .dat文件。

当然,可以通过bash / python /任何脚本来完成,但是我喜欢写性感的单行代码。我所能达到的是(仍然没有任何标准输出):

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

但是xargs中的-a无法理解replace-str'file'。

谢谢你的帮助。


除了这里的其他好答案之外,您还可以使用GNU xargs的-a选项。
James Youngman

Answers:


30

首先,不要将ls输出用作文件列表。使用shell扩展或find。有关ls + xargs滥用的潜在后果以及正确xargs使用示例,请参见下文。

1.简单方法:for循环

如果您只想处理下方的文件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.datmypreciousfile.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. 如果某些文件名包含引号(),则此操作会中断"

可以通过不同的Shell调用方式解决这两个问题:

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"' {} \;

+1表示不解析ls的输出。
rahmu 2011年

2
选项2断裂时的文件名中含有”
thiton

2
很好,谢谢!我会相应地更新。
rozcietrzewiacz,2011年

我只想提的是,如果zsh用作外壳(和SH_WORD_SPLIT未设置),所有讨厌的特殊情况下(白色空间“,在文件名等)需要不被认为是琐碎的。for file in A/*.dat; do ./a.out < $file > ${file%.dat}.ans ; done作品在所有情况下。
jofel

-1是因为我试图弄清楚如何使用stdin执行xargs,而我的问题与文件或无关find
Mehrdad

2

尝试执行以下操作(语法可能会有所不同,具体取决于您使用的shell):

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


那行不通。它将尝试对类似的东西进行操作somefile.dat.dat,并将所有输出重定向到单个文件。
rozcietrzewiacz,2011年

你是对的。我编辑了解决方案以进行更正。
rahmu 2011年

好-差不多了:)只是somefile.dat.ans输出的东西看起来不太好。
rozcietrzewiacz 2011年

1
编辑!我不知道'%'。它的工作原理就像一种魅力,感谢小费。
rahmu 2011年

1
添加a -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 shell内或使用破折号之类的快速shell,则将需要查找任何内容非平凡的),而阅读最合适:

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

这将处理空格,因为read(默认情况下)是面向行的。它不会处理换行符,也不会处理反斜杠,因为默认情况下,它会解释转义序列(实际上允许您传递换行符,但是find无法生成该格式)。许多外壳程序都具有-0读取选项,因此您可以处理所有字符,但不幸的是,它不是POSIX。



1

我认为您至少需要在xargs中进行外壳程序调用:

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

编辑:应该注意的是,当文件名包含空格时,此方法不起作用。不能工作 即使您使用find -0和xargs -0来使xargs正确理解空格,-c shell调用也会破坏它们。但是,OP明确要求使用xargs解决方案,这是我想到的最好的xargs解决方案。如果文件名中的空格可能是一个问题,请使用find -exec或Shell循环。



@rozcietrzewiacz:通常,可以,但是我认为尝试做xargs voodoo的人知道这一点。由于这些文件名经历了另一个Shell扩展,因此无论如何它们都必须表现良好。
thiton 2011年

1
您对Shell扩展错误。ls输出诸如空格之类的东西而不会逃避,就是问题所在。
rozcietrzewiacz,2011年

@rozcietrzewiacz:是的,我知道这个问题。现在,假设您可以正确地转义这些空间并将它们放入xargs中:它们将替换为sh的-c字符串,sh标记化-c字符串,然后一切都会中断。
thiton 2011年

是。您现在看到的,解析ls是不好的。但是xargs 可以安全地与find一起使用-请参阅我的答案中的第二个建议。
rozcietrzewiacz 2011年

1

无需使其复杂化。您可以for循环执行此操作:

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

${file%.dat}.ans位将从.dat文件名中删除文件名后缀$file,而是添加.ans到末尾。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.