【问题标题】:bash: process list of files in chunksbash:处理块中的文件列表
【发布时间】:2012-02-15 05:41:29
【问题描述】:

设置:

我有数百个文件,命名为input0.datinput1.dat、...、input150.dat,我需要使用一些命令cmd(基本上合并所有文件的内容)来处理这些文件。 cmd 将输出文件名作为第一个选项,然后是所有输入文件名的列表:

./cmd output.dat input1.dat input2.dat [...] input150.dat

问题:

问题是由于内存问题,脚本只能处理大约 10 个文件(不要怪我)。因此,不要使用bash 通配符扩展,如

./cmd output.dat *dat

我需要做类似的事情

./cmd temp_output0.dat file0.dat file1.dat [...] file9.dat
[...]
./cmd temp_outputN.dat fileN0.dat fileN1.dat [...] fileN9.dat

之后我可以合并临时输出。

./cmd output.dat output0.dat [...] outputN.dat

如何在bash 中高效地编写脚本?

我试过了,没有成功,例如

for filename in `echo *dat | xargs -n 3`; do [...]; done

问题是这再次同时处理所有文件,因为xargs 的输出行被连接起来了。

编辑:请注意,调用cmd时,我需要指定输出文件名作为第一个命令行参数!

【问题讨论】:

    标签: bash shell batch-processing


    【解决方案1】:

    edit 没有管道或进程替换 - 需要 bash。这能够处理名称中带有空格的文件。使用 bash 数组并在切片中提取:

    i=0
    infiles=(*dat)
    opfiles=()
    while ((${#infiles[@]})); do
        threefiles=("${infiles[@]:0:3}")
        echo ./cmd tmp_output$i.dat "${threefiles[@]}"
        opfiles+=("tmp_output$i.dat")
        ((i++))
        infiles=("${infiles[@]:3}")
    done
    echo ./cmd output.dat "${opfiles[@]}"
    rm "${opfiles[@]}"
    

    使用先进先出 - 这是能够处理文件名中的空格:

    i=0
    opfiles=
    mkfifo /tmp/foo
    echo *dat | xargs -n 3 >/tmp/foo&
    while read threefiles; do
        ./cmd tmp_output$i.dat $threefiles
        opfiles="$opfiles tmp_output$i.dat"
        ((i++)) 
    done </tmp/foo
    rm -f /tmp/foo
    wait
    ./cmd output.dat $opfiles
    rm $opfiles
    

    您需要使用 fifo 来保留 i 变量值以及最终的文件串联集。

    如果你愿意,你可以后台调用./cmd,在最后一次调用cmd之前放一个wait

    i=0
    opfiles=
    mkfifo /tmp/foo
    echo *dat | xargs -n 3 >/tmp/foo&
    while read threefiles; do
        ./cmd tmp_output$i.dat $threefiles&
        opfiles="$opfiles tmp_output$i.dat"
        ((i++)) 
    done </tmp/foo
    rm -f /tmp/foo
    wait
    ./cmd output.dat $opfiles
    rm $opfiles
    

    更新 如果您想完全避免使用 fifo,可以使用进程替换来模拟它,因此将第一个重写为:

    i=0
    opfiles=()
    while read threefiles; do
        ./cmd tmp_output$i.dat $threefiles
        opfiles+=("tmp_output$i.dat")
        ((i++)) 
    done < <(echo *dat | xargs -n 3)
    ./cmd output.dat "${opfiles[@]}"
    rm "${opfiles[@]}"
    

    再次避免管道进入 while,而是从重定向中读取以在 while 循环之后保留 opfiles 变量。

    【讨论】:

    • 是的!这就是我一直在寻找的。谢谢。
    • 这比它真正需要的要复杂得多。可以避免临时文件——只需将xargs 传递到while read。后台处理可能很好,但也可能使事情不必要地复杂化,具体取决于工作的繁重程度等。
    • 如果文件名中有空格,这将不起作用
    • 没错,我添加了一个更新,该更新也适用于名称中包含空格的文件 - 它使用数组,如果您想保持安全,这几乎是唯一的飞行方式。
    【解决方案2】:

    尝试以下方法,它应该适合你:

    echo *dat | xargs -n3 ./cmd output.dat
    

    编辑:回应您的评论:

    for i in {0..9}; do
        echo file${i}*.dat | xargs -n3 ./cmd output${i}.dat
    done
    

    这将一次发送不超过三个文件到./cmd,同时遍历从file00.datfile99.dat 的所有文件,并有10 个不同的输出文件,output1.datoutput9.dat

    【讨论】:

    • 我明白了,我添加了我认为对你有用的东西。你是这个意思吗?
    • 不,实际上,它并不完全正确,因为您对不同的输入文件多次使用相同的输出名称。
    【解决方案3】:

    我正在使用从bash 手册页中找到的快速解决方案。看起来其他人也存在。与xargs -n 不同,它应该正确处理文件名中的空格。

    ls *dat | while readarray -tn 10 tenfiles && ((${#tenfiles[@]}))
    do
      cmd output.dat "${tenfiles[@]}"
    done
    

    【讨论】:

    • 很好的解决方案 - readarray 的文档可以通过 help mapfile 找到。
    【解决方案4】:

    我知道这个问题很久以前就得到了回答和接受,但我发现有一个比目前提供的更简单的解决方案。

    find -name '*.dat' | xargs -n3 | xargs -n3 your_command
    

    要进行更细粒度的控制,或进一步操作字符串,请使用以下形式(根据自己的喜好替换 bash):

    find -name '*.dat' | xargs -n3 | xargs -n3 -I{} sh -c 'your_command {}'
    

    要并行化输出(例如,在 2 个线程上):

    find -name '*.dat' | xargs -n3 | xargs -P2 -n3 -I{} sh -c 'your_command {}'
    

    注意:这不适用于包含空格的文件。

    【讨论】:

    • 通过xargs 两次管道到底是做什么的?
    【解决方案5】:

    GNU Parallel 非常擅长“分块” 以及生成输入/输出文件名和计数器。这将一次占用 3 个文件 (-N3) 并生成一个按顺序编号并包含合并内容的中间输出文件。它会为您并行执行 - 使用您为英特尔支付的所有 CPU 内核:

    parallel -N3 cmd output.{#} {} ::: {1..150}.dat
    

    要查看它的实际效果,请使用--dry-run 选项

    parallel --dry-run -N3 cmd output.{#} {} ::: {1..150}.dat
    

    样本输出

    cmd output.1 1.dat 2.dat 3.dat
    cmd output.2 4.dat 5.dat 6.dat
    cmd output.3 7.dat 8.dat 9.dat
    cmd output.4 10.dat 11.dat 12.dat
    cmd output.5 13.dat 14.dat 15.dat
    cmd output.6 16.dat 17.dat 18.dat
    cmd output.7 19.dat 20.dat 21.dat
    ...
    ...
    cmd output.49 145.dat 146.dat 147.dat
    cmd output.50 148.dat 149.dat 150.dat
    

    【讨论】:

    • 我认为这确实是最好的解决方案,因为它可以让您在分块时轻松访问柜台。这在设置输出文件路径时非常重要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多