【问题标题】:Bash: Trouble when using cat for a lot of filesBash:对大量文件使用 cat 时出现问题
【发布时间】:2013-12-24 22:34:48
【问题描述】:

对于每组参数,我尝试将 400 个数据文件(每个 13 行)合并为一个大文件,如下所示:

folders=(*/)
for folder in ${folders[@]}; do
    #FIND SIGMA
    sig0=${folder#*sig}
    sig=${sig0%amax*}
    #MERGE
    cat sig${sig}amax0.6_incr0.1/tau*.dat > merged_sigma${sig}amax0.6_incr0.1.dat
done

合并后的文件应该有 5200 行,这很简单,但事实并非如此。
相反,每个合并的文件都有不同的行数,大约在 3100 到 5000 之间。

我检查了所有tau*.dat 文件是否存在,不为空并且正好有13 行。 小文件末尾缺少换行符没有问题。在合并文件中,所有行都具有相同的长度。只是一些 - 在我看来是随机的 - 丢失了。

我在某处读到所有文件名中的字符总数不得超过 32767 个字符。但是,即使考虑到文件名不是 tau*.dat 而是 sig0.10amax0.1_incr0.1/tau27.0_sigma0.10__-0.6-0.6_0-0_0.1.dat,每个 cat 命令的字符数也不会超过 25000 个。

【问题讨论】:

  • 假设没有文件名有嵌入空格,计算通配模式扩展的文件数如下:echo sig${sig}amax0.1_incr0.1/tau*.dat | wc -w
  • getconf ARG_MAX 将显示允许的最大字符数,而不是文件名。在像 cygwin 这样的类 unix 安装上,这是 32K 字节,而不是文件名。另请注意,_POSIX_ARG_MAX(标准最小值)为 4096 字节。
  • 回声信号${sig}amax0.1_incr0.1/tau*.dat | wc -w 给出 1 echo sig${sig}amax0.1_incr0.1/tau*.dat | wc -c 给出 25101 ARG_MAX 大约是 200 万。所以关于命令的长度我应该没问题。
  • 如果 echo sig${sig}amax0.1_incr0.1/tau*.dat | wc -w 返回 1,则意味着该模式仅匹配 1 个文件或根本不匹配 - 这与您的 wc -c 语句相矛盾。如果@sputnick 的方法没有帮助,问题一定出在其他地方:(a) globbing 模式是否真的与您认为的所有文件匹配? (b) 所有文件真的都包含 13 行吗? P.S.:作为评论者,我不会自动收到有关进一步 cmets 的通知。要将回复通知特定评论者,请包括@<username>。此外,在反引号中包含一些代码使它们更具可读性。
  • @jimmcnamara 优秀的指针;需要补充两点:(a) 在实践中,限制较低 - in-ulm.de/~mascheck/various/argmax 建议使用以下公式:$(( $(getconf ARG_MAX) - $(env|wc -c) - $(env|wc -l) * 4 - 2048 )); (b) 限制以 bytes 为单位,而不是字符,因此对于 UTF8 编码的文本,characters 的限制可能会更低。

标签: bash limit command-line-arguments cat


【解决方案1】:

我会做什么:

folders=(*/)
for folder in ${folders[@]}; do
    #FIND SIGMA
    sig0=${folder#*sig}
    sig=${sig0%amax*}
    #MERGE
    for file in sig${sig}amax0.1_incr0.1/tau*.dat; do
        cat "$file" >> "merged_sigma${sig}amax0.6_incr0.1.dat"
    done
done

【讨论】:

    【解决方案2】:

    注意:这个答案解释了如何避免使用globbing时命令行过长的问题;但是,命令行长度限制似乎 不是 是 OP 问题的根源。

    要可靠地处理扩展为任意大小的参数列表的 glob - 无需担心遇到命令行长度限制 - 您可以使用以下方法:

    printf '%s\0' *  | xargs -0 ...
    

    这是基于以下假设

    • printf 是(也是)一个 shell builtin,因此 受命令行长度限制(如 getconf ARG_MAX 报告的 - 环境的大小;请参阅http://www.in-ulm.de/~mascheck/various/argmax/) - 至少对于 bash 是正确的(运行 type printf 以验证 printf 是您的 shell 中的内置函数)。
    • xargs 支持-0 选项以接受空字符分隔的输入;请注意,xargs 的核心功能是遵守命令行长度限制并将参数列表划分为指定命令的多次调用(如有必要)。
      • 警告-0 是一个非标准(不符合 POSIX)选项,但它在 Linux 和类似 BSD 的平台(如 OSX)上均受支持。
    • (只有当你知道你的文件名不包含空格(也没有换行符)并且不以-开头时,你才能使用简化形式echo * | xargs ...,同样假设echo是一个内置的shell。)

    如果我们将这种方法应用于 OP 的代码,我们会得到:

    printf '%s\0' sig${sig}amax0.6_incr0.1/tau*.dat | 
      xargs -0 cat > merged_sigma${sig}amax0.6_incr0.1.dat
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 2019-05-25
      • 1970-01-01
      • 2019-01-22
      • 2014-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多