【问题标题】:bash while loop "eats" my space charactersbash while 循环“吃掉”我的空格字符
【发布时间】:2016-08-27 09:40:15
【问题描述】:

我正在尝试解析一个巨大的文本文件,比如 200mb。

文本文件包含一些字符串

123
1234
12345
12345

所以我的脚本看起来像

while read line ; do
echo "$line"
done <textfile

但是使用上述方法,我的字符串 " 12345" 被截断为 "12345"

我尝试过使用

sed -n "$i"p textfile

但吞吐量从每秒 27 行降低到 0.2 行,这是不可接受的 ;-)

任何想法如何解决这个问题?

【问题讨论】:

  • 对不起,我在字符串行前添加了四个0x20,但编辑似乎忽略了它。
  • 那么问题解决了吗?
  • 你根本不应该这样做。请参阅why-is-using-a-shell-loop-to-process-text-considered-bad-practice 并认真考虑使用awk '{print}' textfile 以简洁、健壮、高效、可移植的标准UNIX 方式进行操作。我假设您计划做的不仅仅是打印每一行。
  • 它对每一行的计算速度相当慢,因此从文件中快速读取不是问题,也不是脚本的重要部分。感谢您的提示,我将通读它
  • 如果您使用 awk 而不是 shell 来完成,您提到的相当慢的计算可能会在眨眼之间发生。如果您有兴趣了解正确的方法,请发布后续问题。

标签: bash sed while-loop


【解决方案1】:

这似乎就是你要找的东西:

while IFS= read line; do
echo "$line"
done < textfile

最安全的方法是使用read -r,而不是使用read,后者将跳过对特殊字符的解释(感谢Walter A):

while IFS= read -r line; do
echo "$line"
done < textfile

【讨论】:

  • while IFS= read -r line 是保护输入的最佳方式
【解决方案2】:

您想回显不带字段的行:

while IFS="" read line; do
    echo "$line"
done <<< " 12345"

如果您还想跳过特殊字符的解释,请使用

while IFS="" read -r line; do
    echo "$line"
done <<< " 12345"

IFS 可以不用双引号:

while IFS= read -r line; do
    echo "$line"
done <<< " 12345"

【讨论】:

    【解决方案3】:

    选项 1:

    #!/bin/bash
    
    # read whole file into array
    readarray -t aMyArray < <(cat textfile)
    
    # echo each line of the array
    # this will preserve spaces
    for i in "${aMyArray[@]}"; do echo "$i"; done
    
    • readarray -- 从标准输入读取行
    • -t -- 省略尾随换行符
    • aMyArray -- 存储文件的数组名称
    • cat 文本文件——要存储在变量中的文件

    • for i in "${aMyArray[@]}" -- 对 aMyArray 中的每个元素

    • "" -- 需要在元素中保留空格
    • ${ [@]} -- 引用数组中的所有元素
    • 回显“$i”; -- 对于 "$i" 的每次迭代都回显它
    • "" -- 维护变量空间
    • $i -- 等于数组 aMyArray 的每个元素,因为它循环遍历
    • 完成——关闭循环

    选项 2:

    为了容纳更大的文件,您可以这样做以帮助减轻工作并加快处理速度。

    #!/bin/bash
    
    sSearchFile=textfile
    sSearchStrings="1|2|3|space"
    
    while IFS= read -r line; do
    
        echo "${line}"
    
    done < <(egrep "${sSearchStrings}" "${sSearchFile}")
    

    这将在通过 while 命令循环之前对文件进行 grep(更快)。让我知道这对你有用。请注意,您可以将多个搜索字符串添加到 $sSearchStrings 变量。

    选项 3:

    以及一个多合一的解决方案,将您的搜索条件和其他所有内容结合在一起的文本文件......

    #!/bin/bash
    
    # identify file containing search strings
    sSearchStrings="searchstrings.file"
    
    while IFS= read -r string; do
    
    # if $sSearchStrings empty read in strings
        [[ -z $sSearchStrings ]] && sSearchStrings="${string}"
    # if $sSearchStrings not empty read in $sSearchStrings "|" $string
        [[ ! -z $sSearchStrings ]] && sSearchStrings="${sSearchStrings}|${string}"
    
    # read search criteria in from file
    done <"${sSearchStrings}"
    
    # identify file to be searched
    sSearchFile="text.file"
    
    while IFS= read -r line; do
    
        echo "${line}"
    
    done < <(egrep "${sSearchStrings}" "${sSearchFile}")
    

    【讨论】:

    • @TobySpeight,很公平,谢谢你提醒我要更彻底。我已更新答案以包含更多信息。
    • 感谢您提供的代码。我担心文件是 33gb,因为机器只有 1gb 的内存,这可能会导致一些严重的问题,哈哈。但是我已经为以后的项目存储了代码!
    • @SebastianHeyn,是的,这比上面列出的 ~200Mb 大得多。我不会对那个文件使用这个解决方案。不过,我很高兴你得到了答案!
    • @SebastianHeyn,我使用先前答案的混合体更新了我的答案,这应该可以更快地处理非常大的文本文件。请让我知道结果如何。
    • @jgshawkey 是的,200mb 是一个测试文件;-)
    猜你喜欢
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多