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
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 portavelmenteprintf "$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 argumentoname
padrão pararead
comando shell integrado. - Observe que isso requer que o shell use uma variável padrão para colocar os dados de
read
, o quebash
acontece , mas isso não é padrão. Além disso, seu comandoread
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
awk 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
sh 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
e3
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*
). Oecho
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
echo "${var//,/$'\n'}"
)