【问题标题】:read line by line using `for` loop使用`for`循环逐行读取
【发布时间】:2014-01-22 10:12:33
【问题描述】:

如何使用for 循环逐行读取?

我尝试过的:

hh=$(echo -e "\n1\n2\n3\n4\n")

IFS=$'\n';

for r in "$hh"; do echo $r; done
1 2 3 4

echo -e "$hh"

1
2
3
4

【问题讨论】:

  • 告诉我们更多关于您要解决的问题的信息。您的 comment below 提到了大文件。以上是word splitting中的一个练习

标签: bash loops for-loop


【解决方案1】:

使用while 循环:

$ while read -r r; do echo $r; done <<< "$hh"

1
2
3
4

【讨论】:

  • 当然,但我需要for循环
  • @Orlo 为什么需要for?这里的答案是一个很常见的成语。
  • @BroSlow 是的,有人对此表示反对。不幸的是,我不想告诉你出了什么问题。
  • @Orlo 为什么您特别需要for 循环?
  • @Orlo 大文件是使用 for 循环的充分理由。它要求文本在循环开始之前得到扩展和拆分。它可能需要 shell 在内存中分配两倍甚至三倍的文本大小。
【解决方案2】:

正确答案是,您不要使用 for 循环逐行阅读。使用带有 read 内置函数的 while 循环:

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

【讨论】:

    【解决方案3】:

    虽然使用while read 肯定可以解决这个问题,但是如果你真的想使用for loop 那么你需要使用IFS=$'\n' 来读取bash's for loop 中的输入字符串:

    hh=$(echo -e "\nname n1\nval n2\n3\nfoo n4\n")
    IFS=$'\n' && for r in $hh; do echo "r='$r'"; done
    r='name n1'
    r='val n2'
    r='3'
    r='foo n4'
    

    【讨论】:

    • 更改为echo "t $r" 你会看到它只回显一次
    • @anubhava 还是有问题。当一行包含许多单词时,它将不起作用。它会做的是逐字逐句。这就是为什么你需要 IFS=$'\n'; 这显然不起作用
    • @Orlo:明白你的意思。我根据您的 cmets 更新了我的答案,请查看。
    • 我对这个答案投了反对票,因为它声明 printf 是必需的,这是不正确的。这个解决方案有效的唯一原因是因为$(printf "%s\n" "$hh") 周围没有引号,如果你把引号放在周围,那么这就像原始帖子一样失败。
    • @XTian:是的,同意,printf 并不是真正需要的,如果您看到编辑过的历史记录,您会看到它在此之前的情况。我把它回滚到我以前的版本,因为那更正确。
    【解决方案4】:

    删除 $hh 周围的引号,原始代码可以正常工作:

    hh=$(echo -e "\n1\n2\n3\n4\n")
    IFS=$'\n'
    for r in $hh; do echo "Value: $r"; done
    
    # output:
    Value: 1
    Value: 2
    Value: 3
    Value: 4
    

    【讨论】:

      【解决方案5】:

      只是 for 循环中 $hh 变量的扩展不需要引用。

      此代码有效。

      hh=$(echo -e "\n1\n2\n3\n4\n")
      
      echo "Starting string"
      echo -e "$hh"
      
      IFS=$'\n';
      
      echo "Original Code"
      for r in "$hh"; do echo r is $r; done
      
      
      
      echo "Fixed Code 1"
      
      IFS=$'\n';
      
      for r in "$hh"; do echo r is "$r"; done
      
      
      echo "Fixed Code 2"
      
      IFS=$'\n';
      
      for r in $hh ; do echo r is $r ; done
      

      并产生,

      Starting string
      
      1
      2
      3
      4
      Original Code
      r is 1 2 3 4
      Fixed Code 1
      r is 
      1
      2
      3
      4
      Fixed Code 2
      r is 1
      r is 2
      r is 3
      r is 4
      

      【讨论】:

      • 我的回答是错误的,尽管产生了正确的输出。您的答案不会产生正确的输出。 $r 需要被引用,否则它将删除任何空行并在给定 IFS 的情况下弄乱转义,类似于 a=" a b c "; echo $a 打印 a b c 的方式。
      • 同样的原因需要引用“$hh”。但是,正确循环字符串,这就是为什么在我看来,到目前为止唯一有效的答案是devnull's
      • 多个字段分隔符序列确实被折叠成一个分隔符,这是字段分隔符的一个特征。 OP 的问题是他只有一个回声,我的 FIX 2 给了他 4 个回声,引用 $r 并没有改变这一点。我想你可能正在考虑他的下一个问题。
      • 引用 $r 不会改变它,因为不引用 $hh 已经破坏了它。所以它没有正确打印字符串。
      猜你喜欢
      • 2013-03-28
      • 1970-01-01
      • 2013-10-04
      • 1970-01-01
      • 2011-12-04
      • 2012-01-24
      • 2020-08-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多