Încerc, fără succes, să folosesc o comandă awk
în interiorul unui for
buclă.
Am o variabilă care conține o serie de șiruri pe care vreau să le tai cu awk
pentru a obține datele.
Știu cum să fac asta, dar ceea ce îmi doresc cu adevărat este să tai datele succesiv.
Deci, am această variabilă:
var="data1,data2,data3"
Și aici unde sunt acum:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Încerc să înlocuiesc $1
de bucla $i
dar fără succes.
Comentarii
Răspundeți
Puteți realiza ceea ce sunteți încercând să faceți acest lucru folosind ghilimele duble în scriptul awk pentru a injecta variabila shell în ea. În continuare doriți să păstrați un literal $
, pe care îl puteți face scăpând-o cu backslash :
echo $(awk -F, "{print \$$i}" <<<$var)
Aceasta va extinde $i
la 1
, 2
și 3
în fiecare dintre iterații, prin urmare awk va vedea $1
, $2
și $3
ceea ce îl va face să extindă fiecare dintre câmpuri.
O altă posibilitate este de a injecta variabila shell ca o variabilă awk utilizând semnalizatorul -v
:
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Aceasta atribuie variabila awk i conținutului variabilei shell cu același nume. Variabilele din awk nu folosesc un $
, care este utilizat pentru câmpuri, deci $i
este suficient pentru a se referi la i -câmpul în cazul în care i este o variabilă în awk.
Atribuirea unei variabile awk cu -v
este, în general, mai sigură abordare, în special atunci când poate conține secvențe arbitrare de caractere, în acest caz există un risc mai mic ca conținutul să fie executat ca cod awk împotriva intențiilor dumneavoastră. Dar, deoarece în cazul dvs. variabila deține un singur număr întreg, acest lucru este mai puțin îngrijorător.
O altă opțiune este utilizarea unei bucle for
în awk în sine . Consultați documentația awk (sau căutați pe acest site) pentru mai multe detalii despre cum să faceți acest lucru.
Comentarii
- … sau setați înregistrarea de intrare separator în mod corespunzător
awk -v RS='[,\n]' 1 <<< "$var"
(sau poate mai portabilprintf "$var" | awk -v RS=, 1
) - @steeldriver Acest script awk va imprima toate cele trei câmpuri la Ceea ce este ok, având în vedere că OP face doar asta … Am ajuns să concentrez răspunsul pe extragerea unui singur câmp, presupunând că erau interesați să execute alte comenzi în bucla shell (care ar putea au fost motivația pentru utilizarea unuia în primul rând …)
- Înțeles – de aceea ‘ este motivul pentru care am comentat OP pentru a clarifica ceea ce ei într-adevăr vreau să fac;)
- primul este o injecție (și o problemă de securitate), al doilea o ne (folosind
-v
) nu este o injecție. - Mulțumesc, răspunsul dvs. a rezolvat problema mea! Prima dată postez o întrebare aici după ani de zile citind postări. Nu dezamăgit. O comunitate atât de grozavă!
Răspuns
Utilizarea awk
pare excesiv în această circumstanță, ce zici de un tr
și o buclă de timp:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Ieșire:
data1 data2 data3
Comentarii
- Excelent.
REPLY
fiind argumentul implicitname
pentruread
comanda shell integrată. - Rețineți că acest lucru necesită ca shell-ul să utilizeze o variabilă implicită pentru a introduce datele de la
read
, ceea cebash
se întâmplă să facă , dar acest lucru nu este standard. De asemenea, comandaread
poate modifica datele dacă conține bare oblice și poate elimina spațiile albe din valori. De asemenea, ar citi valorile cu linii noi încorporate ca valori multiple. În plus, aveți nevoie de"$REPLY"
pentru a opri shell-ul să împartă valoarea și să efectueze expansiunea numelui de fișier pe aceasta.
Răspuns
awk poate accepta atât j
(ca variabilă), cât și $j
(ca index de câmp):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
în exemplul” confuz „awk
pe care să îl utilizați (shell sau variabila proprie – având prioritate) ca ambele sunt menționate cu prefixul $
.
notă
sh shell care este standard pentru scriptarea „portabilă” nu acceptă:
(( i=1; i<=3; i++; ))
și <<< $var
construcții
De asemenea, ați putea lua în considerare utilizarea comenzii seq
în for
buclă pentru un control mai fin în generarea secvenței numerice, dacă este disponibilă.
Comentarii
- Bucla dvs. ar funcționa numai dacă s-ar întâmpla să aveți trei fișiere ,
2
și3
în directorul curent. De asemenea, utilizați expansiune variabilă necotată în shell, care poate avea consecințe nedorite dacă datele conțin modele de nume de fișier (cum ar fi*
).echo
poate modifica, de asemenea, datele dacă conține bare oblice.
Răspuns
#!/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
Aici, folosim substituții variabile pentru a obține fiecare câmp de date succesiv delimitat de virgule din valoarea variabilei var
. Prima atribuire către data
din buclă va elimina totul din $var
după prima virgulă. Variabila var
este apoi modificată, astfel încât primul bit până la prima virgulă să fie șters.
Acest lucru continuă până când "$var" = "$data"
ceea ce înseamnă că nu se mai poate face nimic cu șirul.
Acest mod de a face acest lucru ne-ar permite să gestionăm șiruri de date separate prin virgule care conțin linii noi încorporate:
var="line1 line2,data2,last bit goes here"
Cu valorile de mai sus în var
, scriptul de mai sus va genera
data = "line1 line2" data = "data2" data = "last bit goes here"
Nu-i pasă de liniile încorporate; Foarte rar trebuie să repetați invocațiile awk
.
Rețineți că awk
este perfect bucuros să vă citească șirul ca un set de câmpuri delimitate de virgule și că este capabil să facă un loop peste acestea:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Cu var="data1,data2,data3"
, aceasta s-ar imprima
data1 data2 data3
O altă soluție shell care folosește IFS
variabilă pentru a împărți valoarea $var
în biți în timp ce utilizați și set -f
pentru a dezactiva extinderea numelui de fișier:
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'}"
)