合并多个文件,保留 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