Comando awk dentro de um loop for

Tento, sem sucesso, usar um comando awk dentro de um for loop.

Eu tenho uma variável que contém uma série de strings que desejo cortar com awk para obter os dados.

Eu sei fazer isso, mas o que realmente quero é cortar os dados sucessivamente.

Então, eu tenho esta variável:

var="data1,data2,data3" 

E aqui é onde estou agora:

for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done 

Tento substituir o $1 pelo loop $i mas sem sucesso.

Comentários

  • Cada caractere entre um par de aspas simples é tratado como um caractere literal.
  • Qual é o seu caso de uso real? o loop sobre o awk parece desnecessário aqui (por exemplo no bash você pode usar uma substituição de parâmetro echo "${var//,/$'\n'}")
  • Na verdade, a variável conterá urls de um formulário zenity. Quero usar esses urls separadamente, então preciso obter cada um deles independentemente.
  • Relevante, mas não é uma boa solução neste caso: Como fazer atribua valor em tempo de execução no comando AWK

Resposta

Você pode realizar o que você é tentando fazer usando aspas duplas no script awk para injetar a variável shell nele. Você ainda deseja manter um $ literal nele, o que pode ser feito escapando-se com uma barra invertida :

echo $(awk -F, "{print \$$i}" <<<$var) 

Isso irá expandir $i para 1, 2 e 3 em cada uma das iterações, portanto, awk verá $1, $2 e $3 que o fará expandir cada um dos campos.

Outra possibilidade é injetar a variável shell como um variável awk usando o sinalizador -v :

echo $(awk -F, -v i="$i" "{print $i}" <<<$var) 

Isso atribui a variável awk i ao conteúdo da variável shell com o mesmo nome. Variáveis em awk não usam um $, que é usado para campos, então $i é o suficiente para se referir ao i -ésimo campo se i for uma variável no awk.

Atribuir uma variável awk com -v geralmente é um método mais seguro abordagem, particularmente quando pode conter sequências arbitrárias de caracteres, nesse caso há menos risco de que o conteúdo seja executado como um código awk contra suas intenções. Mas, como no seu caso a variável contém um único inteiro, isso não é tão preocupante.

Outra opção é usar um loop for no próprio awk . Consulte a documentação do awk (ou pesquise neste site) para obter mais detalhes sobre como fazer isso.

Comentários

  • … ou defina o registro de entrada separador apropriadamente awk -v RS='[,\n]' 1 <<< "$var" (ou talvez mais portavelmente printf "$var" | awk -v RS=, 1)
  • @steeldriver Esse script awk imprimirá todos os três campos em uma vez. O que está ok, dado que o OP está fazendo exatamente isso … Acabei focando a resposta na extração de um único campo, supondo que eles estivessem interessados em executar outros comandos no loop de shell (o que pode foram a motivação para usar um em primeiro lugar …)
  • Compreendido – foi por isso ‘ por que comentei o OP para esclarecer o que eles realmente quero fazer;)
  • O primeiro é uma injeção (e uma preocupação de segurança), o segundo o ne (usando -v) não é uma injeção.
  • Obrigado, sua resposta resolveu meu problema! Primeira vez que posto uma pergunta aqui, depois de anos lendo postagens. Não fiquei desapontado. Que comunidade incrível!

Resposta

Usar awk parece excessivo nesta circunstância, que tal um tr e um loop while:

tr , "\n" <<<"$var" | while read; do echo $REPLY done 

Resultado:

data1 data2 data3 

Comentários

  • Ótimo. REPLY sendo o argumento name padrão para read comando shell integrado.
  • Observe que isso requer que o shell use uma variável padrão para colocar os dados de read, o que bash acontece , mas isso não é padrão. Além disso, seu comando read pode modificar os dados se contiver barras invertidas e pode retirar os espaços em branco de flanco dos valores. Ele também leria valores com novas linhas incorporadas como valores múltiplos. Além disso, você precisa de "$REPLY" para impedir que o shell divida o valor e execute a expansão do nome do arquivo nele.

Resposta

pode aceitar ambos j (como variável) e $j (como índice de campo):

 for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done  

$i no exemplo” confused “awk qual usar (shell ou sua própria variável – tendo precedência), pois ambos são referidos com $ prefixo.

nota

shell que é padrão para scripts “portáteis” não é compatível:

(( i=1; i<=3; i++; )) e <<< $var construções

Além disso, você pode considerar o uso do comando seq em for loop para controle mais preciso na geração de sequência numérica, se disponível.

Comentários

  • Seu loop só funcionaria se você tivesse três arquivos chamados , 2 e 3 no diretório atual. Além disso, você usa a expansão de variável não citada no shell, o que pode ter consequências indesejadas se os dados contiverem padrões de nome de arquivo (como *). O echo pode, além disso, modificar os dados se eles contiverem barras invertidas.

Resposta

#!/bin/sh var="data1,data2,data3" unset data while [ "$var" != "$data" ]; do data=${var%%,*} # delete first comma and the bit after it var=${var#*,} # delete bit up to first comma (and the comma) printf "data = "%s"\n" "$data" done 

Aqui, usamos substituições de variáveis para obter cada campo de dados delimitado por vírgulas sucessivos do valor da variável var. A primeira atribuição a data no loop removerá tudo de $var após a primeira vírgula. A variável var é então modificada de forma que o primeiro bit até a primeira vírgula seja excluído.

Isso continua até "$var" = "$data" o que significa que nada mais pode ser feito com a string.

Esta maneira de fazer isso nos permitiria lidar com strings de dados separadas por vírgulas que contêm novas linhas incorporadas:

var="line1 line2,data2,last bit goes here" 

Com os valores acima em var, o script acima geraria

data = "line1 line2" data = "data2" data = "last bit goes here" 

Não se preocupando com novas linhas incorporadas; Você muito raramente tem que repetir invocações de awk.

Observe que awk fica perfeitamente feliz em ler sua string como um conjunto de campos delimitados por vírgulas e que é capaz de fazer um loop sobre estes:

printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }" 

Com var="data1,data2,data3", isso imprimiria

data1 data2 data3 

Outra solução de shell que faz uso de IFS variável para dividir o valor $var em bits, enquanto também usa set -f para desativar a expansão do nome do arquivo:

set -f oldIFS=$IFS; IFS="," set -- $var IFS=$oldIFS; unset oldIFS set +f for data do printf "data = "%s"\n" "$data" done 

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *