【问题标题】:Bash loop, print current iteration?Bash循环,打印当前迭代?
【发布时间】:2012-06-12 03:46:55
【问题描述】:

假设你有一个简单的循环

while read line
do
  printf "${line#*//}\n"
done < text.txt

有没有一种优雅的方法可以用输出打印当前迭代?类似的东西

0 的 1 快速 2棕色 3 狐狸

我希望避免在每个循环中设置变量并递增它。

【问题讨论】:

    标签: bash loops while-loop


    【解决方案1】:

    为此,您需要在每次迭代时增加一个计数器(就像您试图避免的那样)。

    count=0
    while read -r line; do
       printf '%d %s\n' "$count" "${line*//}"
       (( count++ ))
    done < test.txt
    

    编辑:经过深思熟虑,如果您有 bash 版本 4 或更高版本,则无需计数器即可:

    mapfile -t arr < test.txt
    for i in "${!arr[@]}"; do
       printf '%d %s' "$i" "${arr[i]}"
    done
    

    内置的mapfile 将文件的全部内容读入数组。然后,您可以遍历数组的索引,这将是行号并访问该元素。

    【讨论】:

    • 不错的解决方案,但如果您正在处理大文件,mapfile 可能会非常消耗内存。在不知道 OP 的文件大小的情况下,我建议使用通过管道运行的东西,因此与文件大小无关。
    【解决方案2】:

    您不经常看到它,但您可以在while 循环的条件子句中有多个命令。以下仍然需要一个明确的计数器变量,但这种安排可能更适合或适合某些用途。

    while ((i++)); read -r line
    do
        echo "$i $line"
    done < inputfile
    

    无论最后一条命令返回什么都满足while 条件(在本例中为read)。

    有些人喜欢在同一行包含do。这就是它的样子:

    while ((i++)); read -r line; do
        echo "$i $line"
    done < inputfile
    

    【讨论】:

    • @jordanm:for 做了类似的事情:for ((i=0, j=14; i&lt;=20; i++, j+=11))。此外,您可以一次从文件中读取两行(或多行):while read -r oddline; read -r evenline; do echo "odd: [$oddline] even: [$evenline]"; done &lt; filename
    【解决方案3】:
    n=0
    cat test.txt | while read line; do
      printf "%7s %s\n" "$n" "${line#*//}"
      n=$((n+1))
    done
    

    当然,这也适用于 Bourne shell。

    如果你真的想避免增加一个变量,你可以通过 grep 或 awk 管道输出:

    cat test.txt | while read line; do
      printf " %s\n" "${line#*//}"
    done | grep -n .
    

    awk '{sub(/.*\/\//, ""); print NR,$0}' test.txt
    

    【讨论】:

    • 已编辑以包含您最终标准的选项。
    • 我更喜欢 awk 解决方案,虽然 sub(/.*\/\//, ""); ${line#*//} 相同
    • 嗯......你的答案的第一部分与 jordanm 的几乎相同,另外两个似乎工作得很好。将其归结为 StackOverflow 的随机性。 :-)
    • cat 是无用的,没有-rread 将破坏输入中的反斜杠。这是重新实现nl 的一种非常低效的方法。
    【解决方案4】:

    可以使用范围来遍历,可以是数组、字符串、输入行或列表。

    在这个例子中,我使用了一个数字列表 [0..10] 也使用了 2 的增量。

    #!/bin/bash
    for i in {0..10..2}; do 
       echo " $i times"
    done
    

    输出是:

     0 times
     2 times
     4 times
     6 times
     8 times
     10 times
    

    要打印索引而不考虑循环范围,您必须使用变量“COUNTER=0”并在每次迭代“COUNTER+1”中增加它。

    我的解决方案打印每次迭代,FOR 遍历输入行并每次迭代递增一,还显示输入行中的每个单词:

    #!/bin/bash 
    
    COUNTER=0
    line="this is a sample input line"
    
    for word in $line; do        
        echo "This i a word number $COUNTER: $word"
        COUNTER=$((COUNTER+1))
    done
    

    输出是:

    This i a word number 0: this
    This i a word number 1: is
    This i a word number 2: a
    This i a word number 3: sample
    This i a word number 4: input
    This i a word number 5: line
    

    查看更多关于循环的信息:enter link description here

    测试您的脚本:enter link description here

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    【解决方案5】:

    更新:此处发布的其他答案更好,尤其是@Graham 和@DennisWilliamson 的答案。

    类似这样的东西应该适合:

    tr -s ' ' '\n' <test.txt | nl -ba
    

    如果您想从 0 开始索引,可以在 nl 命令中添加 -v0 标志。

    【讨论】:

    • 我在 FreeBSD、OSX 或 Illumos 中没有n1 命令。哪里来的?
    • 啊 - 讨厌的等宽字体。 @thb,我认为您仍然需要像${line#*//} 那样将线剥离到//。但是您当然可以通过nl 传递while 循环,就像我在回答中对grep -n . 所做的那样。
    猜你喜欢
    • 2020-04-26
    • 2016-12-21
    • 2012-03-12
    • 1970-01-01
    • 2015-05-04
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多