bash:以块为单位处理文件列表

fue*_*zig 5 bash shell batch-processing

那个设定:

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

./cmd output.dat input1.dat input2.dat [...] input150.dat
Run Code Online (Sandbox Code Playgroud)

问题:

问题是由于内存问题,脚本只能处理10个左右的文件(不要怪我)。因此,不要bash像这样使用通配符扩展名

./cmd output.dat *dat
Run Code Online (Sandbox Code Playgroud)

我需要做类似的事情

./cmd temp_output0.dat file0.dat file1.dat [...] file9.dat
[...]
./cmd temp_outputN.dat fileN0.dat fileN1.dat [...] fileN9.dat
Run Code Online (Sandbox Code Playgroud)

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

./cmd output.dat output0.dat [...] outputN.dat
Run Code Online (Sandbox Code Playgroud)

如何有效地编写脚本bash

我尝试过但没有成功,例如

for filename in `echo *dat | xargs -n 3`; do [...]; done
Run Code Online (Sandbox Code Playgroud)

问题在于这会再次处理所有文件,因为xargsget 的输出行是连接在一起的。

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

drr*_*lvn 5

尝试以下操作,它应该对您有用:

echo *dat | xargs -n3 ./cmd output.dat
Run Code Online (Sandbox Code Playgroud)

编辑:回应您的评论:

for i in {0..9}; do
    echo file${i}*.dat | xargs -n3 ./cmd output${i}.dat
done
Run Code Online (Sandbox Code Playgroud)

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


Pet*_*esh 5

编辑没有管道或进程替换 - 需要 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[@]}"
Run Code Online (Sandbox Code Playgroud)

使用 fifo - 这无法处理文件名中的空格:

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
Run Code Online (Sandbox Code Playgroud)

您需要使用 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
Run Code Online (Sandbox Code Playgroud)

更新 如果您想完全避免使用 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[@]}"
Run Code Online (Sandbox Code Playgroud)

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