【问题标题】:How do I iterate over each line in a file with Bash?如何使用 Bash 遍历文件中的每一行?
【发布时间】:2012-03-24 04:03:25
【问题描述】:

给定一个包含多行的文本文件,我想遍历 Bash 脚本中的每一行。我曾尝试使用cut,但cut 不接受\n(换行符)作为分隔符。

这是我正在使用的文件的示例:

one
two 
three 
four

有谁知道我如何在 Bash 中循环遍历此文本文件的每一行?

【问题讨论】:

    标签: bash loops iteration


    【解决方案1】:

    我发现自己遇到了同样的问题,这对我有用:

    cat file.cut | cut -d$'\n' -f1
    

    或者:

    cut -d$'\n' -f1 file.cut
    

    【讨论】:

    • 为什么会这样?在这种情况下美元符号有什么作用
    • @JulesG.M.它之所以有效,是因为 Bash 有一个称为 ANSI-C 引用的功能。文档中提到了这一点:“$'string' 形式的单词被特殊处理。单词扩展为字符串,并按照 ANSI C 标准的规定替换反斜杠转义字符。”。你可以在这里找到文档:gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
    【解决方案2】:

    使用cat 进行连接或显示。这里不需要。

    file="/path/to/file"
    while read line; do
      echo "${line}"
    done < "${file}"
    

    【讨论】:

    • 我同意这一点,但这明显比cut 方法慢。
    • @StanStrum 这个解决方案应该比cut 方法更快(O(n) 时间复杂度)并且更具可读性。 OP 询问如何遍历文件中的每一行。使用cut,必须知道文件中的行数并使用cut 编写那么多语句,或者编写一个while 循环给你O(n + n²) 时间复杂度。
    • 这个解决方案并不完整,因为会出现一些陷阱和边缘情况。
    【解决方案3】:

    简单使用:

    echo -n `cut ...`
    

    这会抑制末尾的 \n

    【讨论】:

      【解决方案4】:
      cat FILE|while read line; do # 'line' is the variable name
         echo "$line" # do something here
      done
      

      或(见评论):

      while read line; do # 'line' is the variable name
         echo "$line" # do something here
      done < FILE
      

      【讨论】:

      • UUOC - 使用while ... done &lt; file
      • 它不是没用,它可能是浪费(在阅读本段之后),但它也更具IMO可读性。
      【解决方案5】:

      因此,已经提供了一些非常好的(可能更好)的答案。但是看看原始问题的措辞,在想要使用 BASH for 循环时,令我惊讶的是,没有人提到改变 Field Separator IFS 的解决方案。这是一个纯 bash 解决方案,就像接受的读取行一样

      old_IFS=$IFS
      IFS='\n'
      for field in $(<filename)
      do your_thing;
      done
      IFS=$old_IFS
      

      【讨论】:

        【解决方案6】:

        如果您确定输出将始终以换行符分隔,请使用 head -n 1 代替 cut -f1(请注意,您在脚本中提到了 for 循环,而您的问题最终与脚本无关)。

        许多其他答案,包括已接受的答案,都不必要地多行。无需在多行上执行此操作或更改系统上的默认分隔符。

        另外,Ivan 用-d$'\n' 提供的解决方案在Mac OSX 或CentOS 7 上都不适用于我。由于他的回答已经有四年了,我认为$ 字符的逻辑一定有所改变对于这种情况。

        【讨论】:

          【解决方案7】:

          While 循环输入重定向和read 命令。

          您不应该使用cut 对文件中的每一行执行顺序迭代,因为cut 不是为此而设计的。

          将每个 FILE 中选定的部分行打印到标准输出。 — man cut

          TL;DR

          您应该在 IFS 设置为 \n 的函数范围内使用 while 循环和 read -r 命令并将标准输入重定向到您的文件,并在使用时使用 -E echo.

          processFile() {          # Function scope to prevent overwriting IFS globally
            file="$1"              # Any file that exists
            local IFS="\n"         # Allows spaces and tabs
            while read -r line; do # Read exits with 1 when done; -r allows \
              echo -E "$line"      # -E allows printing of \ instead of gibberish
            done < $file           # Input redirection allows us to read file from stdin
          }
          processFile /path/to/file
          

          迭代

          为了遍历文件的每一行,我们可以使用 while 循环。这将使我们可以根据需要进行多次迭代。

          while <condition>; do
            <body>
          done
          

          让我们的文件准备好阅读

          我们可以使用read 命令将标准输入中的一行存储在一个变量中。在我们可以使用它从文件中读取一行之前,我们需要重定向标准输入以指向我们的文件。我们可以通过输入重定向来做到这一点。根据man pages for bash,重定向的语法是[fd]&lt;file,其中fd 默认为标准输入(又名文件描述符0)。我们可以将它放在 while 循环之前或之后。

          while <condition>; do
            <body>
          done < /path/to/file
          
          # or the non-traditional way
          </path/to/file while <condition>; do
            <body>
          done
          

          读取文件并结束循环

          现在我们的文件可以从标准输入读取,我们可以使用read。在我们的上下文中read 的语法是read [-r] var...,其中-r 保留\(反斜杠)字符,而不是将其用作转义序列字符,var 是存储变量的名称输入。您可以有多个变量来存储输入的片段,但我们只需要一个变量来读取整行。除此之外,要保留来自echo 的任何输出中的任何反斜杠,您可能需要使用-E 标志来禁用对反斜杠转义的解释。如果您有任何缩进(空格或制表符),您需要暂时将IFS(输入字段分隔符)变量更改为仅"\n";通常设置为" \t\n"

          main() {
            local IFS="\n"
            read -r line
            echo -E "$line"
          }
          
          main
          

          我们如何使用read 来结束我们的while循环?

          据我所知,实际上只有一种可靠的方法可以确定您何时完成使用read 读取文件:检查read 的退出值。如果read 的退出值为0,那么我们成功读取了一行,如果它是1 或更高,那么我们到达了EOF(文件结束)。考虑到这一点,我们可以在 while 循环的条件部分调用read

          processFile() {
            # Could be any file you want hardcoded or dynamic
            file="$1"
          
            local IFS="\n"
            while read -r line; do
              # Process line here
              echo -E "$line"
            done < $file
          }
          
          processFile /path/to/file1
          processFile /path/to/file2
          

          A visual breakdown of the above code via Explain Shell.

          【讨论】:

            【解决方案8】:

            我的观点是“cut”使用 '\n' 作为其默认分隔符。 如果要使用cut,我有两种方法:

                cut -d^M -f1 file_cut
            

            我通过在 Ctrl+V 后单击 Enter 来制作 ^M。另一种方法是

                cut -c 1- file_cut
            

            这有帮助吗?

            【讨论】:

            • 不幸的是,以上方法都不适合我(bash,版本 4.2.45)。
            • cut 不使用 '\n' 作为其默认分隔符。
            猜你喜欢
            • 1970-01-01
            • 2020-02-02
            • 1970-01-01
            • 2021-01-16
            • 1970-01-01
            • 2013-12-26
            • 1970-01-01
            • 2013-04-08
            • 2010-09-14
            相关资源
            最近更新 更多