bash for number with numbers names

时间:2013-03-03 15:00:58

标签: macos bash for-loop batch-processing cat

我目前正在开展一个数学项目,只是碰到了bash编程的砖墙。

目前我有一个包含800个文本文件的目录,我想要做的是运行一个循环到 cat 前80个文件(_01到_80)到一个新文件并保存到其他地方,然后是下一个80(_81到_160)文件等。

目录中的所有文件都如下所示:ath_01,ath_02,ath_03等。

有人可以帮忙吗?

到目前为止,我有:

#!/bin/bash

for file in /dir/*

do
echo ${file}
done

简单地列出我的文件。我知道我需要使用cat file1 file2> newfile.txt不知何故,但它让我与_01,_02等的数字扩展混淆。

如果我将文件的名称更改为使用下划线以外的其他内容,会有帮助吗?喜欢ath.01等?

干杯,

4 个答案:

答案 0 :(得分:5)

由于您提前知道您拥有多少文件以及它们的编号方式,因此可能更容易“展开循环”,可以这么说,并使用复制粘贴和一些手动调整来编写一个使用大括号扩展的脚本。

#!/bin/bash

cat ath_{001..080} > file1.txt
cat ath_{081..160} > file2.txt
cat ath_{161..240} > file3.txt
cat ath_{241..320} > file4.txt
cat ath_{321..400} > file5.txt
cat ath_{401..480} > file6.txt
cat ath_{481..560} > file7.txt
cat ath_{561..640} > file8.txt
cat ath_{641..720} > file9.txt
cat ath_{721..800} > file10.txt

或者,使用嵌套的for循环和seq命令

N=800
B=80
for n in $( seq 1 $B $N ); do
    for i in $( seq $n $((n+B - 1)) ); do
       cat ath_$i
    done > file$((n/B + 1)).txt
done

外部循环将迭代n到1,81,161等。内部循环将迭代i超过1到80,然后是81到160等。内部循环的主体如果i文件到标准输出,只是转储内容,但循环的聚合输出存储在文件1,然后是2,等等。

答案 1 :(得分:4)

您可以尝试这样的事情:

cat "$file" >> "concat_$(( ${file#/dir/ath_} / 80 ))"
  • 使用${file#/dir/ath_}从文件名
  • 中删除前缀/dir/ath_
  • $(( / 80 ))后缀除以80(整数除法)

还要将循环更改为

for file in /dir/ath_*

所以你只得到你需要的文件

答案 2 :(得分:3)

如果你想要80个文件的组,你最好确保名字是可排序的;这就是经常使用前导零的原因。假设您在文件名中只有一个下划线,并且名称中没有换行符,则:

SOURCE="/path/to/dir"
TARGET="/path/to/other/directory"
(
cd $SOURCE || exit 1
ls |
sort -t _ -k2,2n |
awk -v target="$TARGET" \
    '{ file[n++] = $1
       if (n >= 80)
       {
           printf "cat"
           for (i = 0; i < 80; i++)
               printf(" %s", file[i]
           printf(" >%s/%s.%.2d\n", target, "newfile", ++number)
           n = 0
       }
     END {
       if (n > 0)
       {
           printf "cat"
           for (i = 0; i < n; i++)
               printf(" %s", file[i]
           printf(" >%s/%s.%.2d\n", target, "newfile", ++number)
       }
     }' |
sh -x
)

指定了两个目录(文件所在的位置以及摘要的位置);该命令将目录更改为源目录(800文件所在的位置)。它列出了名称(如果需要,可以指定一个glob模式)并对它们进行数字排序。输出被输入awk,它会动态生成shell脚本。它一次收集80个名称,然后生成cat命令,将这些文件复制到单个目标文件,例如"newfile.01";调整printf()命令以适合您自己的命名/编号约定。然后将shell命令传递给shell进行执行。

在测试时,请将sh -x替换为空,或sh -vn或类似内容。只有在您确定它可以执行您想要的操作时才添加活动shell。请记住,shell脚本在运行时位于源目录中。

表面上看,xargs命令很好用;困难在于协调输出文件号。可能有一种方法可以使用-n 80选项一次对80个文件进行分组,并使用某种奇特的方式生成调用号,但我不知道它。

另一种选择是使用xargs -n来执行shell脚本,该脚本可以通过列出目标目录中已有的内容来推断出正确的输出文件号。这在很多方面都会更清洁:

SOURCE="/path/to/dir"
TARGET="/path/to/other/directory"
(
cd $SOURCE || exit 1
ls |
sort -t _ -k2,2n |
xargs -n 80 cpfiles "$TARGET"
)

cpfiles的样子:

TARGET="$1"
shift
if [ $# -gt 0 ]
then
    old=$(ls -r newfile.?? | sed -n -e 's/newfile\.//p; 1q')
    new=$(printf "%.2d" $((old + 1)))
    cat "$@" > "$TARGET/newfile. $new
fi

零参数的测试避免了xargs使用零参数执行命令的麻烦。总的来说,我更喜欢使用awk的解决方案。

答案 3 :(得分:1)

这是@ chepner第一个解决方案的宏,使用GNU Make作为模板语言:

SHELL := /bin/bash
N = 800
B = 80

fileNums = $(shell seq 1 $$((${N}/${B})) )
files = ${fileNums:%=file%.txt}

all: ${files}

file%.txt : start = $(shell echo $$(( ($*-1)*${B}+1 )) )
file%.txt : end = $(shell echo $$(( $* * ${B} )) )

file%.txt:
        cat ath_{${start}..${end}} > $@

使用:

$ make -n all
cat ath_{1..80} > file1.txt
cat ath_{81..160} > file2.txt
cat ath_{161..240} > file3.txt
cat ath_{241..320} > file4.txt
cat ath_{321..400} > file5.txt
cat ath_{401..480} > file6.txt
cat ath_{481..560} > file7.txt
cat ath_{561..640} > file8.txt
cat ath_{641..720} > file9.txt
cat ath_{721..800} > file10.txt