【问题标题】:Why can't you use cat to read a file line by line where each line has delimiters为什么你不能使用 cat 逐行读取文件,其中每行都有分隔符
【发布时间】:2013-06-10 14:14:54
【问题描述】:

我有一个包含如下内容的文本文件:

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma

我写了一个脚本

for i in `cat file`
do
   echo $i
done

由于某种原因,脚本的输出不会逐行输出文件,而是在逗号和换行符处将其断开。为什么 cat 或 "for blah in cat xyz" 这样做,我怎样才能让它不这样做?我知道我可以使用

while read line
do
   blah balh blah
done < file

但我想知道为什么 cat 或“for blah in”这样做是为了加深我对 unix 命令的理解。 Cat 的手册页对我没有帮助,在 bash 手册中查找或循环也没有得到任何答案(http://www.gnu.org/software/bash/manual/bashref.html)。提前感谢您的帮助。

【问题讨论】:

    标签: linux bash for-loop cat


    【解决方案1】:

    IFS - 可以设置内部字段分隔符以获得您想要的。

    要一次读取整行,请使用: IFS=""

    【讨论】:

      【解决方案2】:

      for 循环加上内部字段分隔符 (IFS) 的更改将按预期读取文件

      用于输入

      abc 123, comma
      the quick brown fox
      jumped over the lazy dog
      comma, comma
      

      For 循环加上 IFS 更改

      old_IFS=$IFS
      IFS=$'\n'
      for i in `cat file`
      do
              echo $i
      done
      IFS=$old_IFS
      

      结果

      abc 123, comma
      the quick brown fox
      jumped over the lazy dog
      comma, comma
      

      【讨论】:

      • 只需使用IFS= read -r line 保留行中的所有空格。
      • while 循环“丢失”间距的唯一原因是您使用了echo $line 而不是echo "$line"。如果间距很重要,请将变量引用用双引号括起来。
      • 正如 chepner 所说,这应该是 read -r 以避免意外的副作用(评估反斜杠转义序列)。
      【解决方案3】:

      您可以使用IFS 变量来指定您想要换行符作为字段分隔符:

      IFS=$'\n'
      for i in `cat file`
      do
         echo $i
      done
      

      【讨论】:

      • 不安全——你已经阻止了字符串拆分,但你没有阻止全局扩展。如果一行包含*,则在回显期间将扩展为当前目录中的名称列表。
      【解决方案4】:

      问题不在于cat,也不在于for 循环本身;它是在使用反引号。当你写任何一个时:

      for i in `cat file`
      

      或(更好):

      for i in $(cat file)
      

      或(bash):

      for i in $(<file)
      

      shell 执行命令并将输出捕获为字符串,在$IFS 中的字符处分隔单词。如果您想将行输入到$i,则必须摆弄IFS 或使用while 循环。如果存在处理的文件很大的危险,while 循环会更好;与使用$(...) 的版本不同,它不必一次将整个文件读入内存。

      IFS='
      '
      for i in $(<file)
      do echo "$i"
      done
      

      "$i" 周围的引号通常是个好主意。在这种情况下,使用修改后的$IFS,实际上并不重要,但好习惯就是好习惯。它在以下脚本中很重要:

      old="$IFS"
      IFS='
      '
      for i in $(<file)
      do
         (
         IFS="$old"
         echo "$i"
         )
      done
      

      当数据文件包含多个单词之间的空格时:

      $ cat file
      abc                  123,         comma
      the   quick   brown   fox
      jumped   over   the   lazy   dog
      comma,   comma
      $ 
      

      输出:

      $ sh bq.sh
      abc                  123,         comma
      the   quick   brown   fox
      jumped   over   the   lazy   dog
      comma,   comma
      $
      

      没有双引号:

      $ cat bq.sh
      old="$IFS"
      IFS='
      '
      for i in $(<file)
      do
         (
         IFS="$old"
         echo $i
         )
      done
      $ sh bq.sh
      abc 123, comma
      the quick brown fox
      jumped over the lazy dog
      comma, comma
      $
      

      【讨论】:

      • 感谢您的帮助和回复。我对 bash/*nix 有点困惑。我没有改变IFS。它默认设置为换行符。我用 echo "IFS = $IFS word test" 检查了它,字符串 "word test" 打印到下一行,所以我们知道它默认是 \n 。在任何情况下,使用默认的 IFS,即使 IFS=\n,它也会在逗号处中断我的行。当我按照您上面的建议进行操作时,通过将 IFS 明确设置为 \n,它会打印我的整行而不会中断逗号。知道为什么它在显式设置为 \n 时起作用,而在默认情况下 IFS 已经是 \n 时不起作用?再次感谢。
      • IFS的默认值为(使用一段bash-speak)$' \t\n';也就是说,它由空白、制表符、换行符组成。这可能会改变您的分析。我相信,当您说“在逗号处中断”时,您的意思是它在逗号后的空格处中断,这与包含空白(以及制表符和换行符)的 IFS 一致。
      【解决方案5】:
      cat filename | while read i
      do
          echo $i
      done
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-21
        相关资源
        最近更新 更多