bash:处理块中文件的列表

时间:2012-01-20 17:04:48

标签: bash shell batch-processing

设置:

我有几百个文件,名称类似于input0.datinput1.dat,...,input150.dat,我需要使用某个命令cmd进行处理(基本上合并所有文件的内容)。 cmd将输出文件名作为第一个选项,然后是所有输入文件名的列表:

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

问题:

问题是由于内存问题,脚本只能处理10个左右的文件(不要因此而责怪我)。因此,而不是像{/ 1>那样使用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时将输出文件名指定为第一个命令行参数!

5 个答案:

答案 0 :(得分:3)

你可以这样做:

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变量。

答案 1 :(得分: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.dat传递到file99.dat,并拥有10个不同的输出文件{{1} } output1.dat

答案 2 :(得分:2)

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

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 {}'

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

GNU并行“分块” 并生成输入/输出文件名和计数器方面非常出色。这将一次获取3个文件(-N3),并生成一个中间输出文件,该文件按顺序编号并包含合并的内容。它为您并行完成-利用您为Intel付出了如此之高的所有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