【问题标题】:Bash: How to tail then copy multiple files (eg using xargs)?Bash:如何复制多个文件(例如使用 xargs)?
【发布时间】:2017-01-13 23:52:30
【问题描述】:

我一直在尝试 xargs 和管道的各种组合,但我无法得到正确的结果。以前的问题并没有完全涵盖我想要做什么:

  • 我在某处有一个源目录,比如说 /foo/source,其中混合了不同的文件
  • 我只想将在源中找到的 csv 文件复制到其他目标,例如 /foo/dest
  • 但我还需要同时删除 232 个标题行(例如使用尾部)

我发现我需要将 find 的结果通过管道传输到 xargs 中,然后 xargs 可以对每个 find 结果运行命令。但我正在努力追尾然后复制。如果我通过管道将 tail 导入 cp,cp 似乎没有接收到文件(缺少文件操作数)。以下是我迄今为止尝试过的一些示例:

find /foo/source -name "*.csv" | xargs -I '{}' sh -c 'tail -n +232 | cp -t /foo/dest'

cp: 缺少文件操作数

find /foo/source -name "*.csv" | xargs -I '{}' sh -c 'tail -n +232 {} | cp -t /foo/dest'

结果:

cp: 无法访问'/foo/dest': 没有这样的文件或目录...

find /foo/source -name "*.csv" | xargs -I '{}' sh -c 'tail -n +232 {} > /foo/dest/{}'

sh: /foo/dest/foo/source/0001.csv: 没有这样的文件或目录...

任何指针将不胜感激!

谢谢

【问题讨论】:

    标签: linux bash find tail xargs


    【解决方案1】:

    只需使用 findexec 并将文件名复制到变量中:

    find your_dir -name "*.csv" -exec sh -c 'f="$1"; tail -n +5 "$f" > dest_dir/$(basename "$f")' -- {} \;
    

    参见f={} 使$f 包含文件的名称和完整路径。然后,将tail 的输出重定向到文件中,从中剥离路径。

    或者,基于以下 cmets 中的Random832's suggestion(谢谢!):

    find your_dir -name "*.csv" -exec sh -c 'tail -n +5 "$1" > dest_dir/$(basename "$1")' -- {} \;
    

    【讨论】:

    • @JamesAllen 好的,现在我找到了另一种使用 -exec 的方法。
    • 感谢@fedorqui,Barmar 使用 xargs 和 basename 击败了您。不过谢谢!
    • @JamesAllen 是的,我总是想知道什么是最好的,通过管道发送到xargs 或使用-exec。我更喜欢后者,但在大多数情况下,这两种方式应该是等价的。
    • 对可能包含空格或其他特殊字符的文件名使用 exec 将无法正常工作。使用-exec sh -c 'f="$1"; ...' -- {} \;(或者在整个shell命令中使用"$1",不要使用$f变量)
    • @fedorqui 实际上我偶然发现了它的需要并假设它总是需要的,但事实证明-c '...' 之后的第一个参数被放入 $0。但是 $0 有一个特殊的含义,所以我不建议使用它来省略 -- 参数。
    【解决方案2】:

    您的最后一个命令已关闭,但问题是 {} 被替换为完整路径名,而不仅仅是文件名。使用basename 命令从中提取文件名。

    find /foo/source -name "*.csv" | xargs -I '{}' sh -c 'tail -n +232 {} > /foo/dest/$(basename {})'
    

    【讨论】:

    • 谢谢,我在玩basename,但语法不正确!大力帮助,谢谢
    【解决方案3】:

    作为 find 和 xargs 的替代方案,您可以使用 for 循环,作为 tail 的替代方案,您可以使用 sed,请考虑以下几点:

    source=/foo/source
    dest=/foo/dest
    for csv in $source/*.csv; do sed '232,$ !d' $csv > $dest/$(basename $csv); done
    

    【讨论】:

      【解决方案4】:

      使用 GNU Parallel 你会这样做:

      find /foo/source -name "*.csv" | parallel tail -n +232 {} '>' /foo/dest/{/}
      

      【讨论】:

        猜你喜欢
        • 2013-03-09
        • 2017-03-12
        • 1970-01-01
        • 2020-03-04
        • 2013-05-22
        • 2015-07-09
        • 1970-01-01
        • 2014-03-24
        • 1970-01-01
        相关资源
        最近更新 更多