确定 bash for 循环中的最后一次迭代

确定 bash for 循环中的最后一次迭代

我有一个循环将一些行添加到配置文件中:

for i in ${H//,/ }
do
        sed -i "7i\                      host($i) or" $configPath/$g.conf
done

$H 是逗号分隔的变量,例如:host1,host2,host4,host10

它返回以下内容:

                  host(host10) or
                  host(host4) or
                  host(host2) or
                  host(host1) or

但我想要实现的是:

                  host(host10) or
                  host(host4) or
                  host(host2) or
                  host(host1)

或相反亦然:

                  host(host1) or
                  host(host2) or
                  host(host4) or
                  host(host10)

谁能帮助我找到正确的方向,我怎样才能做到这一点?

答案1

这个问题在大多数编程语言中都以相同的形式出现。以某种方式跳过后缀会很复杂。我不会讨论 shell 语法,只是用伪代码概述人们通常处理这个问题的方式:

# get it over with at the beginning:
print a[0] (no newline)
loop from a[1] onwards:
   print "or\n"
   print a[i]
print "\n" #terminate the last one

这种情况可以通过循环索引或仅循环所有元素(跳过第一个)来工作,因为不涉及索引测试(如果语言支持对数组的直接迭代,例如 bash 和 python)。

你也可以从0开始,跳过最后一个,在循环外处理(上面的镜像),但是跳过最后一个通常比较脏,而且可能是不可能的(要跳过最后一个,你必须知道它是最后一个,但第一个总是可以立即跳过)。例如,如果您正在从流中读取元素,您无法提前知道哪一个是最后一个,而这种形式是唯一的选择

另一种方式:

# test a loop counter
N = length of array
loop with indices:
   if i==N-1:
      print a[i]
   else:
      print a[i],"or"

请注意,这种情况需要您知道有多少个元素,并且它会不断测试索引。因此,您必须循环索引,或者单独跟踪索引(在循环之前设置 i=0,在循环内部设置 i++)。

我把这个写成一个菜谱,我将把 bash 的实现留给你。

答案2

从我看来,您只需要对代码进行一些细微的更改:只需将“或”放入第一个循环期间为空的变量中即可。

operator=''
for i in ${H//,/ }
do
        sed -i "7i\                      host($i)$operator" "$configPath/$g.conf"
        operator=' or'
done

答案3

您通常不想使用 shell 循环来进行文本处理。在这里,我会使用awk

export H
awk 'NR == 7 {
  s = ENVIRON["H"]
  gsub(/,/, ") or\n   host(", s)
  print "   host(" s ")"}
  {print}' file

就像 GNUsed具有-i就地编辑选项一样,GNU awk(自 4.1.0 起)也具有-i /usr/share/awk/inplace.awk1 作为等效选项。

如果您想编辑文本文件,有人可能会认为您应该使用文本编辑器。使用vim,您可以通过以下方式实现相同的方法:

export H
vim -esc '
  let @a=$H
  6put a
  s/.*/host(&)/
  s/,/) or\rhost(/g
  x' file

^不使用-i inplaceas尝试首先从当前工作目录gawk加载inplace扩展(asinplace或),有人可能已经在其中植入了恶意软件。随系统提供的扩展inplace.awk的路径可能会有所不同,请参阅输出inplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

答案4

我没有使用原始问题中的“sed”,而是编写了一个带有 echo 的更简单的问题来说明这一想法:而不是循环遍历空格分隔的项目:

  • 首先我们得到“,”替换后的字数来确定迭代次数
  • 使用“()”将空白字符串转换为数组
  • 迭代计数
  • 将“或”部分作为条件的输出并放入变量中
  • 通过迭代对数组进行子集化
  • 还有 echo/sed 等

    #!/bin/bash
    
    H="host1,host2,host3"
    H2=${H//,/ }
    N=`echo $H2 | wc -w`
    H3=($H2)
    
    for ((i = 1; i <= $N; i++))
    do
        if [ "$i" -eq "$N" ]; then OR=""; else OR=" or"; fi
        ITEM=${H3[$i-1]}
        echo "$ITEM$OR"
    done
    

相关内容