合并多个文件,保留 unix 中的原始序列
Merge multiple files preserving the original sequence in unix
我在目录中有多个(超过100个)文本文件,例如
files_1_100.txt
files_101_200.txt
文件的内容是一些变量的名称,例如 files_1_100.txt
包含一些介于 1 到 100 之间的变量名称
"var.2"
"var.5"
"var.15"
同样files_201_300.txt
包含一些101到200之间的变量
"var.203"
"var.227"
"var.285"
和files_1001_1100.txt
作为
"var.1010"
"var.1006"
"var.1025"
我可以使用命令合并它们
cat files_*00.txt > ../all_files.txt
但是,文件的内容并不遵循父文件中的内容。例如 all_files.txt
显示
"var.1010"
"var.1006"
"var.1025"
"var.1"
"var.5"
"var.15"
"var.203"
"var.227"
"var.285"
那么,我怎样才能确保 files_1_100.txt
的内容排在第一位,然后是 files_201_300.txt
,然后是 files_1001_1100.txt
,这样 all_files.txt
的内容就是
"var.1"
"var.5"
"var.15"
"var.203"
"var.227"
"var.285"
"var.1010"
"var.1006"
"var.1025"
让我试试看,但我认为这会起作用:
ls file*.txt | sort -n -t _ -k2 -k3 | xargs cat
我们的想法是获取文件列表并对它们进行排序,然后将它们传递给 cat 命令。
排序使用了几个选项:
- -n - 使用数字排序而不是字母排序
- -t _ - 使用下划线字符将输入(文件名)分成字段
- -k2 -k3 - 首先按第二个字段排序,然后按第三个字段排序(2 个数字)
您说过您的文件被命名为 file_1_100.txt、file_101_201.txt 等。如果这意味着(似乎表明)第一个数字 "chunk" 始终是唯一的,那么你可以去掉 -k3
标志。仅当您最终使用 file_100_2.txt 和 file_100_10.txt 时才需要该标志,您必须查看第二个数字 "chunk" 以确定首选顺序。
根据您正在处理的文件数量,您可能会发现指定 glob (file*.txt) 可能会淹没 shell 并导致关于行太长的错误。如果是这种情况,您可以这样做:
ls | grep '^file.*\.txt$' | sort -n -t _ -k2 -k3 | xargs cat
cat file_*
的默认排序行为是按字母顺序排列,而不是数字排列。
按数字顺序列出它们,然后对每一个进行 cat,将输出附加到某个文件。
ls -1| sort -n |xargs -i cat {} >> file.out
您可以使用 printf
sort
并将其通过管道传输到 xargs cat
:
printf "%s[=10=]" f*txt | sort -z -t_ -nk2 | xargs -0 cat > ../all_files.txt
请注意,整个管道正在处理以 NULL 结尾的文件名,因此确保此命令甚至可以处理具有 space/newlines 等的敌人文件名。
如果您的文件名没有任何特殊字符或空格,那么其他答案应该是简单的解决方案。
否则,试试这种基于 rename
的方法:
$ ls files_*.txt
files_101_200.txt files_1_100.txt
$ rename 's/files_([0-9]*)_([0-9]*)/files_000_000/;s/files_0*([0-9]{3})_0*([0-9]{3})/files__/' files_*.txt
$ ls files_*.txt
files_100_100.txt files_101_200.txt
$ cat files_*.txt > outputfile.txt
$ rename 's/files_0*([0-9]*)_0*([0-9]*)/files__/' files_*.txt
您可以尝试使用 for 循环并一个一个地添加文件(当数字未用零填充时 -v 会正确排序文件)
for i in $(ls -v files_*.txt)
do
cat $i >> ../all_files.txt
done
或者一行更方便:
for i in $(ls -v files_*.txt) ; do cat $i >> ../all_files.txt ; done
您也可以使用 Awk 通过拆分和排序来做到这一点 ARGV
:
awk 'BEGIN {
for(i=1; i<=ARGC-1; i++) {
if(i > 1) {
j=i-1
split(ARGV[i], curr, "_")
split(ARGV[j], last, "_")
if (curr[2] < last[2]) {
tmp=ARGV[i]
ARGV[i]=ARGV[j]
ARGV[j]=tmp
}
}
}
}1' files_*00.txt
我在目录中有多个(超过100个)文本文件,例如
files_1_100.txt
files_101_200.txt
文件的内容是一些变量的名称,例如 files_1_100.txt
包含一些介于 1 到 100 之间的变量名称
"var.2"
"var.5"
"var.15"
同样files_201_300.txt
包含一些101到200之间的变量
"var.203"
"var.227"
"var.285"
和files_1001_1100.txt
作为
"var.1010"
"var.1006"
"var.1025"
我可以使用命令合并它们
cat files_*00.txt > ../all_files.txt
但是,文件的内容并不遵循父文件中的内容。例如 all_files.txt
显示
"var.1010"
"var.1006"
"var.1025"
"var.1"
"var.5"
"var.15"
"var.203"
"var.227"
"var.285"
那么,我怎样才能确保 files_1_100.txt
的内容排在第一位,然后是 files_201_300.txt
,然后是 files_1001_1100.txt
,这样 all_files.txt
的内容就是
"var.1"
"var.5"
"var.15"
"var.203"
"var.227"
"var.285"
"var.1010"
"var.1006"
"var.1025"
让我试试看,但我认为这会起作用:
ls file*.txt | sort -n -t _ -k2 -k3 | xargs cat
我们的想法是获取文件列表并对它们进行排序,然后将它们传递给 cat 命令。
排序使用了几个选项:
- -n - 使用数字排序而不是字母排序
- -t _ - 使用下划线字符将输入(文件名)分成字段
- -k2 -k3 - 首先按第二个字段排序,然后按第三个字段排序(2 个数字)
您说过您的文件被命名为 file_1_100.txt、file_101_201.txt 等。如果这意味着(似乎表明)第一个数字 "chunk" 始终是唯一的,那么你可以去掉 -k3
标志。仅当您最终使用 file_100_2.txt 和 file_100_10.txt 时才需要该标志,您必须查看第二个数字 "chunk" 以确定首选顺序。
根据您正在处理的文件数量,您可能会发现指定 glob (file*.txt) 可能会淹没 shell 并导致关于行太长的错误。如果是这种情况,您可以这样做:
ls | grep '^file.*\.txt$' | sort -n -t _ -k2 -k3 | xargs cat
cat file_*
的默认排序行为是按字母顺序排列,而不是数字排列。
按数字顺序列出它们,然后对每一个进行 cat,将输出附加到某个文件。
ls -1| sort -n |xargs -i cat {} >> file.out
您可以使用 printf
sort
并将其通过管道传输到 xargs cat
:
printf "%s[=10=]" f*txt | sort -z -t_ -nk2 | xargs -0 cat > ../all_files.txt
请注意,整个管道正在处理以 NULL 结尾的文件名,因此确保此命令甚至可以处理具有 space/newlines 等的敌人文件名。
如果您的文件名没有任何特殊字符或空格,那么其他答案应该是简单的解决方案。
否则,试试这种基于 rename
的方法:
$ ls files_*.txt
files_101_200.txt files_1_100.txt
$ rename 's/files_([0-9]*)_([0-9]*)/files_000_000/;s/files_0*([0-9]{3})_0*([0-9]{3})/files__/' files_*.txt
$ ls files_*.txt
files_100_100.txt files_101_200.txt
$ cat files_*.txt > outputfile.txt
$ rename 's/files_0*([0-9]*)_0*([0-9]*)/files__/' files_*.txt
您可以尝试使用 for 循环并一个一个地添加文件(当数字未用零填充时 -v 会正确排序文件)
for i in $(ls -v files_*.txt)
do
cat $i >> ../all_files.txt
done
或者一行更方便:
for i in $(ls -v files_*.txt) ; do cat $i >> ../all_files.txt ; done
您也可以使用 Awk 通过拆分和排序来做到这一点 ARGV
:
awk 'BEGIN {
for(i=1; i<=ARGC-1; i++) {
if(i > 1) {
j=i-1
split(ARGV[i], curr, "_")
split(ARGV[j], last, "_")
if (curr[2] < last[2]) {
tmp=ARGV[i]
ARGV[i]=ARGV[j]
ARGV[j]=tmp
}
}
}
}1' files_*00.txt