Macos 带数字名称的bash for循环
我目前正在做一个数学项目,在bash编程时遇到了一些麻烦 目前我有一个包含800个文本文件的目录,我想做的是运行一个循环,以cat将前80个文件(_01到_80)保存到一个新文件中并保存到其他地方,然后将下80个(_81到_160)文件等 目录中的所有文件如下所示:ath_01、ath_02、ath_03等 有人能帮忙吗 到目前为止,我已经:Macos 带数字名称的bash for循环,macos,bash,for-loop,batch-processing,cat,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 它只是简单地列出了我的文件。我知道我需要以某
#!/bin/bash
for file in /dir/*
do
echo ${file}
done
它只是简单地列出了我的文件。我知道我需要以某种方式使用cat file1 file2>newfile.txt,但它会将我的扩展名与_01、_02等数字扩展名混淆
如果我将文件名更改为使用下划线以外的内容,会有帮助吗?比如ath.01等等
干杯,您可以尝试以下方式:
cat "$file" >> "concat_$(( ${file#/dir/ath_} / 80 ))"
- 使用
可以从文件名中删除前缀${file}/dir/ath}
/dir/ath
将后缀除以$(/80))
(整数除法)80
for file in /dir/ath_*
因此,您只获得所需的文件如果您想要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
)
其中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
零参数测试可以避免使用零参数执行一次命令时出现的问题。总的来说,我更喜欢这个解决方案,而不是使用
awk
的解决方案,因为你提前知道你有多少文件以及它们是如何编号的,所以可以说“展开循环”,使用复制粘贴和一些手动调整来编写一个使用大括号扩展的脚本可能更容易
#!/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中,等等。下面是@chepner第一个解决方案的宏,使用gnumake
作为模板语言:
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
有趣的如果我理解正确,您可以为每个输入文件运行一次
cat
;您通过将输入文件编号除以80来选择目标文件,因此每80个文件目标文件都会发生变化。@JonathanLeffler。但这个答案只有在文件按顺序编号时才有效。这取决于非按顺序编号的文件的“工作”定义。您将1..80个文件分组在一起,完全基于文件名中嵌入的数字;我的备选方案将1..80个文件组合在一起,而不考虑数字序列中的间隔。在问题没有明确方向的情况下,它们是“等价但不同”的。啊,我没想到会这样运行它。干杯+1这个答案可能更快,因为它涉及更少的cat调用。它还可以处理文件名编号方面的差异,感谢您对如何将所有文件名组合在一起的深入解释。+1很好的方法。如果您不喜欢,请查看更改并还原。谢谢,太好了!谢谢你!您不能删除内部循环并使用eval将代码扩展到初始硬编码解决方案吗?然后,每个输出文件只调用一次Cat。eval
是一种安全风险,我认为它是无法以任何其他方式完成的任务的最后手段。
$ 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