Determine a última iteração em um bash para loop

3

Eu tenho um loop que adiciona algumas linhas em um arquivo de configuração:

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

$ H é uma variável delimitada por vírgula, ex: host1, host2, host4, host10

Retorna o seguinte:

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

mas o que eu quero alcançar é:

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

ou vice-versa:

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

Quem pode me ajudar a me colocar na direção certa? Como posso fazer isso?

    
por Sinan 13.12.2016 / 14:23

5 respostas

3

Esse problema surge na maioria das linguagens de programação no mesmo formato. É uma complicação para pular o sufixo de alguma forma. Eu não vou entrar na sintaxe do shell, mas apenas delinear no pseudo-código as maneiras pelas quais as pessoas geralmente lidam com isso:

# 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

Este caso funciona por meio de um loop de índices, ou simplesmente passando por todos os elementos (ignorando o primeiro), já que nenhum teste de índice está envolvido (se o idioma suportar iteração direta sobre matrizes, como bash e python). / p>

Você também pode começar de 0 e pular o último, e lidar com ele fora do loop (a imagem espelhada acima), mas pular o último é geralmente mais sujo e pode ser impossível (pular o último, você deve saber que é o último, mas o primeiro sempre pode ser ignorado imediatamente). Por exemplo, se você estiver lendo elementos do fluxo, não saberá com antecedência qual é o último, e esse formulário é a única opção !

Do outro jeito:

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

Observe que este caso exige que você saiba quantos elementos existem e testa constantemente o índice. Por este motivo, você deve fazer um loop sobre os índices ou manter o controle do índice separadamente (definindo i = 0 antes do loop e do i ++ dentro).

Eu escrevi isso como uma receita, deixarei a implementação bash para você.

    
por 13.12.2016 / 16:27
2

Você geralmente não deseja usar loops de shell para processar texto. Aqui, eu usaria awk :

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

Como o GNU sed tem a opção -i para edição no local, o% GNU awk tem -i inplace como equivalente.

Se você quiser editar um arquivo de texto, pode-se argumentar que você deve usar um editor de texto. Com vim , você poderia implementar a mesma abordagem com:

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

Pelo que vejo, você só precisa de uma pequena alteração no seu código: basta colocar "ou" em uma variável vazia durante o primeiro loop.

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

Em vez do "sed" na pergunta original, escrevi uma mais simples com echo para ilustrar a ideia: em vez de percorrer os itens separados por espaços em branco:

  • Primeiro, obtemos a contagem de palavras após "," substituição para determinar o número de iterações
  • Converta a string whitespaced em uma matriz com "()"
  • Iterar por meio de contagens
  • Torne "ou" parte uma saída de uma condição e em uma variável
  • Subconjunto da matriz com iteração
  • E echo / sed, etc

    #!/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
    
por 13.12.2016 / 17:26
0

Graças à resposta da orion com a explicação e o pseudo-código, consegui criar o seguinte script BaSH, usado para a descoberta do índice Zabbix ElasticSearch, que obtém todos os índices do ElasticSearch e imprime um objeto JSON válido: (para o objeto JSON validate Preciso do último elemento sem uma vírgula , , então coloco todos os elementos em um array, pulo a primeira iteração e manejo como último fora do loop:

#!/bin/bash
# Zabbix-Agent discovery for ElasticSearch indices
# UserParameter=es.indices.discovery,/etc/zabbix/es-indices-discovery.sh
indices=($(echo $(curl -n --silent -XGET localhost:9200/_cat/indices?h=index | egrep -v "^\.")))

echo -e "{"
echo -e "\t\"data\":["
for i in "${!indices[@]}"; do
    if [ $i == 0 ]; then continue; fi
    echo -en "\t\t{"
    echo -en "\t\"{#INDEXNAME}\":\"${indices[$i]}\""
    echo -e "\t\t},"
done
echo -en "\t\t{"
echo -en "\t\"{#INDEXNAME}\":\"${indices[0]}\""
echo -e "\t\t}"

echo -e "\t]"
echo "}"

saída:

{
        "data":[
                {       "{#INDEXNAME}":"index2"         },
                {       "{#INDEXNAME}":"index3"         },
                {       "{#INDEXNAME}":"index4"         },
                {       "{#INDEXNAME}":"firstindex"             }
        ]
}
    
por 09.08.2017 / 01:09

Tags