Provo, senza successo, a utilizzare un comando awk
allinterno di un for
loop.
Ho “una variabile che contiene una serie di stringhe che voglio tagliare con awk
per ottenere i dati.
So come farlo, ma quello che voglio veramente è tagliare i dati successivamente.
Quindi ho questa variabile:
var="data1,data2,data3"
Ed ecco dove sono adesso:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Provo a sostituire $1
dal ciclo $i
ma senza successo.
Commenti
Risposta
Puoi ottenere ciò che vuoi cercando di farlo utilizzando le virgolette doppie nello script awk per iniettare la variabile di shell al suo interno. Vuoi comunque mantenere un $
letterale al suo interno, cosa che puoi fare facendo lescape con la barra rovesciata :
echo $(awk -F, "{print \$$i}" <<<$var)
Questo espanderà $i
a 1
, 2
e 3
in ciascuna delle iterazioni, pertanto awk vedrà $1
, $2
e $3
che espanderà ciascuno dei campi.
Unaltra possibilità è iniettare la variabile di shell come variabile awk utilizzando il flag -v
:
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
che assegna la variabile awk i al contenuto della variabile di shell con lo stesso nome. Le variabili in awk non utilizzano un $
, utilizzato per i campi, quindi $i
è sufficiente per fare riferimento a i -esimo campo se i è una variabile in awk.
Assegnare una variabile awk con -v
è generalmente più sicuro approccio, in particolare quando può contenere sequenze arbitrarie di caratteri, in tal caso cè meno rischio che il contenuto venga eseguito come codice awk contro le tue intenzioni. Ma poiché nel tuo caso la variabile contiene un singolo numero intero, questo è meno preoccupante.
Unaltra opzione è usare un ciclo for
in awk stesso . Consulta la documentazione di awk (o cerca nel sito) per maggiori dettagli su come farlo.
Commenti
- … o imposta il record di input separatore in modo appropriato
awk -v RS='[,\n]' 1 <<< "$var"
(o forse in modo più portabileprintf "$var" | awk -v RS=, 1
) - @steeldriver Quello script awk stamperà tutti e tre i campi in una volta. Il che è ok dato che lOP sta facendo proprio questo … ho finito per concentrare la risposta sullestrazione di un singolo campo supponendo che fossero interessati a eseguire altri comandi nel ciclo della shell (che potrebbe sono stati la motivazione per usarne uno in primo luogo …)
- Capito – che ‘ è il motivo per cui ho commentato lOP per chiarire cosa davvero vuole fare;)
- Il primo è uniniezione (e un problema di sicurezza), il secondo o ne (utilizzando
-v
) non è uniniezione. - Grazie, la tua risposta ha risolto il mio problema! La prima volta che posto una domanda qui dopo anni di lettura di post. Non deluso. Una community così fantastica!
Rispondi
Usare awk
sembra eccessivo in questa circostanza, che ne dici di un tr
e di un ciclo while:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Risultato:
data1 data2 data3
Commenti
- Ottimo.
REPLY
è ilname
argomento predefinito perread
comando della shell incorporato. - Tieni presente che ciò richiede che la shell utilizzi una variabile predefinita per inserire i dati da
read
, cosa che accade abash
, ma questo non è standard. Inoltre, il tuo comandoread
potrebbe modificare i dati se contengono barre rovesciate e potrebbe rimuovere gli spazi bianchi dai valori. Leggerebbe anche i valori con newline incorporati come valori multipli. È inoltre necessario"$REPLY"
per impedire alla shell di suddividere il valore e di eseguire lespansione del nome del file su di esso.
Risposta
awk può accettare sia j
(come variabile) e $j
(come indice di campo):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
nellesempio” confused “awk
quale usare (shell o la propria variabile – ha la precedenza) poiché entrambi sono indicati con il prefisso $
.
nota
sh che è standard per lo scripting “portabile” non supporta:
(( i=1; i<=3; i++; ))
e <<< $var
costrutti
Potresti anche prendere in considerazione lutilizzo del comando seq
in for
loop per un controllo più preciso nella generazione di sequenze numeriche, se disponibile.
Commenti
- Il tuo loop funzionerebbe solo se ti capita di avere tre file chiamati ,
2
e3
nella directory corrente. Inoltre, utilizzi lespansione delle variabili senza virgolette nella shell che potrebbe avere conseguenze indesiderate se i dati contengono modelli di nomi di file (come*
).echo
può inoltre modificare i dati se contengono barre rovesciate.
Risposta
#!/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
In questo caso, utilizziamo sostituzioni di variabili per ottenere ogni successivo campo di dati delimitato da virgole dal valore della variabile var
. La prima assegnazione a data
nel ciclo rimuoverà tutto da $var
dopo la prima virgola. La variabile var
viene quindi modificata in modo da eliminare il primo bit fino alla prima virgola.
Questo continua fino a "$var" = "$data"
il che significa che non si può fare altro sulla stringa.
Questo modo di farlo ci consentirebbe di gestire stringhe di dati separate da virgole che contengono newline incorporate:
var="line1 line2,data2,last bit goes here"
Con i valori di cui sopra in var
, lo script precedente produrrebbe
data = "line1 line2" data = "data2" data = "last bit goes here"
Non preoccuparsi dei newline incorporati; molto raramente devi eseguire il ciclo di invocazioni di awk
.
Tieni presente che awk
è perfettamente felice di leggere la tua stringa come un insieme di campi delimitati da virgole e che è in grado di scorrere questi:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Con var="data1,data2,data3"
, verrebbe stampato
data1 data2 data3
Unaltra soluzione di shell che utilizza IFS
per dividere il valore $var
in bit utilizzando anche set -f
per disabilitare lespansione del nome file:
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'}"
)