【问题标题】:Concatenating text files in bash在 bash 中连接文本文件
【发布时间】:2014-09-16 00:53:02
【问题描述】:

我在一个文件夹中有许多文本文件,其中只有一行浮点值,我想在 bash 中将它们连接起来,例如:file_1.txt、file_2.txt ...file_N.txt。我想将它们按从 1 到 N 的顺序放在一个 txt 文件中。有人可以帮我吗?这是我拥有的代码,但它只是以随机方式连接它们。谢谢

for file in *.txt
do 
  cat ${file} >>  output.txt  
done 

【问题讨论】:

标签: bash


【解决方案1】:

这对我有用...

for i in $(seq 0 $N); do [[ -f file_$i.txt ]] && cat file_$i.txt; done > newfile

或者,更简洁

for i in $(seq 0 $N); do cat file_$i.txt 2> /dev/null ;done > newfile

【讨论】:

  • +1 是一种聪明的替代方法,但您需要知道最大值。提前编号。
【解决方案2】:

两种解决方案都适用于当前的特定案例,但不是一般,因为它们会用嵌入空格或其他元字符的文件名打断(这些字符在使用时不加引号,对shell具有特殊意义)。

这里是使用带有嵌入空格等的文件名的解决方案。:


首选解决方案适用于支持sort -zxargs -0 的系统(例如,Linux、OSX、*BSD):

printf "%s\0" file_*.txt | sort -z -t_ -k2,2n  | xargs -0 cat > out.txt

使用 NUL(空字符,0x0)分隔文件名,从而安全地保留它们的边界。

这是最强大的解决方案,因为它甚至可以正确处理嵌入了 newlines 的文件名(尽管这样的文件名在实践中非常罕见)。不幸的是,sort -zxargs -0符合 POSIX。


POSIX 兼容解决方案,使用xargs -I

printf "%s\n" file_*.txt | sort -t_ -k2,2n  | xargs -I % cat % > out.txt

处理是基于的,由于使用-Icat每个输入文件名调用一次,这使得该方法比上述方法慢.

【讨论】:

    【解决方案3】:
    for file in *.txt
    do 
      cat ${file} >>  output.txt  
    done 
    

    这对我也有效:

    for file in *.txt
    do 
      cat $file >>  output.txt  
    done
    

    你不需要{}

    但更简单的还是:

    cat file*.txt > output.txt
    

    因此,如果评论中建议的文件超过 9 个,则可以执行以下操作之一:

    files=$(ls file*txt | sort -t"_" -k2g)
    files=$(find . -name "file*txt" | sort -t "_" -k2g)
    files=$(printf "%s\n" file_*.txt | sort -k1.6n) # Thanks to glenn jackman
    

    然后:

    cat $files
    

    cat $(find . -name "file*txt" | sort -t "_" -k2g)
    

    最好还是正确编号文件,如果文件少于 100 个则为 file_01.txt,少于 1000 个则为 file_001.txt,依此类推。


    示例:

    ls file*txt
    file_1.txt  file_2.txt  file_3.txt  file_4.txt  file_5.txt  file_10.txt
    

    它们只包含相应的编号。

    $ cat $files
    1
    2
    3
    4
    5
    10
    

    【讨论】:

    • 这仅对 {1..9} 中的 N 有效。一旦 N 超过 9,文件将无法正确排序。
    • 另一种使用排序的方法:如果您知道数字从文件名的第 6 个字符开始:printf "%s\n" file_*.txt | sort -k1.6n
    • printf "%s\n" file_*.txt 是更可取的形式:ls file_*.txt 做同样的事情,但不必要地调用外部可执行文件。 find . -name "file_*txt",除了调用外部可执行文件外,还可能做一些不同的事情,因为它处理整个子树(即子目录中的文件也可以捡起;添加-maxdepth 1 以避免这种情况)。
    • 它适用于 this 情况,但请注意,如果要将排序限制为 single 字段,则必须指定该字段索引 两次,例如:-k2,2g - 否则,该字段和行的其余部分充当排序键。此外,除非您的数字不是十进制或具有 + 前缀或指数符号,否则请使用 n 而不是 g 进行数字排序(避免舍入错误,速度更快 - 请参阅 goo.gl/X6KeE)。因此,排序键应该是:-k2,2n-k1.6,1n。在 this 的情况下不是问题,但总体而言值得注意:此解决方案将破坏具有嵌入空格的文件名。
    【解决方案4】:

    正如其他人指出的那样,如果您有文件 file_1file_2file_3...file_123283,这些文件的内部 BASH 排序会将 file_11 放在 file_2 之前,因为它们'按文本而非数字重新排序。

    您可以使用sort 获取您想要的订单。假设你的文件是file_#...

    cat $(ls -1 file_* | sort -t_ -k2,2n)
    
    • ls -1 每行列出一个文件。
    • sort -t_ 表示用下划线分解排序字段。这使得第二个排序字段成为文件名的数字部分。
    • -k2,2n 表示按数字按第二个字段排序。

    然后,将所有文件连接在一起。

    一个问题是,如果您有大量文件,您最终可能会填满命令行缓冲区。在cat 获取文件名之前,$(...) 必须先展开。

    【讨论】:

    • +1 用于解释和正确且可移植的sort 命令。狡辩:printf '%s\n' file_* 优于 ls -1 file_*。在 this 的情况下不是问题,但总体而言值得注意:此解决方案将破坏具有嵌入空格的文件名。
    【解决方案5】:

    尽管我建议不要解析 ls 的输出,但我们开始吧。

    ls 有一个“版本排序”选项,可以根据需要对编号文件进行排序。请参阅下面的演示。

    要连接,您需要:

    ls -v file*.txt | xargs cat > output
    
    $ touch file{1..20}.txt
    $ ls
    file1.txt   file12.txt  file15.txt  file18.txt  file20.txt  file5.txt  file8.txt
    file10.txt  file13.txt  file16.txt  file19.txt  file3.txt   file6.txt  file9.txt
    file11.txt  file14.txt  file17.txt  file2.txt   file4.txt   file7.txt
    $ ls -1
    file1.txt
    file10.txt
    file11.txt
    file12.txt
    file13.txt
    file14.txt
    file15.txt
    file16.txt
    file17.txt
    file18.txt
    file19.txt
    file2.txt
    file20.txt
    file3.txt
    file4.txt
    file5.txt
    file6.txt
    file7.txt
    file8.txt
    file9.txt
    $ ls -1v
    file1.txt
    file2.txt
    file3.txt
    file4.txt
    file5.txt
    file6.txt
    file7.txt
    file8.txt
    file9.txt
    file10.txt
    file11.txt
    file12.txt
    file13.txt
    file14.txt
    file15.txt
    file16.txt
    file17.txt
    file18.txt
    file19.txt
    file20.txt
    

    【讨论】:

    • 这可能是一个 GNU ls 选项。要在没有 ls 的情况下执行此操作:printf "%s\n" file*.txt | sort -V | xargs cat > output
    • sort -V 相同,也必须是 GNU 选项。
    • @glennjackman:我以前见过,今天也见过。你有想出简单而优雅的解决方案的诀窍。 :) +1
    • 总结可移植性方面:ls -v 是 GNU 扩展,sort -V 也是如此。 OSX 与大多数其他情况不同,实际上 使用 GNU sort,但版本太旧 (5.93)。 (ls -v 存在于 OSX 上,但意味着不同的东西 - 奇怪的是它在 *BSD 系统上不存在)。在 this 情况下不是问题,但总的来说值得注意:此解决方案将破坏具有嵌入空格的文件名。
    【解决方案6】:

    使用这个:

    find . -type f -name "file*.txt" | sort -V | xargs cat -- >final_file
    

    如果文件被编号,那么排序就不会以我们人类期望的自然方式发生。为此,您必须将 -V 选项与 sort 命令一起使用。

    【讨论】:

    • 您可能应该将-maxdepth 1 添加到find 命令,以避免在子目录 中可能匹配文件。也就是说,printf '%s\n' file*.txt 可能更容易。请注意,-VGNU sort 扩展。在 this 情况下不是问题,但一般值得注意:会与嵌入空格的文件名中断。
    【解决方案7】:

    您可以使用 ls 列出文件:

    for file in `ls *.txt`
    do·
      cat ${file} >>  output
    done
    

    这里讨论了一些排序技术:Unix's 'ls' sort by name

    【讨论】:

    • 它也对我有用,但我不能按名称顺序排列它们。我的输出 txt 文件从文件 10 到 19 开始,然后是 1,20,2-9,但我想让它们按数字顺序从 1 到 20。谢谢
    • 您必须将文件从 name_1.txt 重命名为 name_01.txt。
    • 创建类似这样的文件:触摸文件{01..20}.txt
    • 除了不解决排序问题:直接使用通配符(路径名扩展)更简单、更健壮、更快:for file in *.txt - 解析ls 输出是不是一个好主意;见mywiki.wooledge.org/ParsingLs。如所写(未排序),您的命令可以简化为:cat *.txt > output
    • 但是这个问题是关于排序问题的。 IMO 问题仅在于文件命名和使用 1 而不是 01。如果文件命名正确,简单 cat*.txt > out 就足够了。
    猜你喜欢
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 2012-11-16
    • 2023-03-03
    相关资源
    最近更新 更多