Comandă Awk într-o buclă for

Î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

  • Fiecare caracter între o pereche de ghilimele este tratat ca un caracter literal.
  • Care este cazul dvs. real de utilizare? buclarea peste awk pare inutilă aici (pentru exaple în bash, puteți utiliza o înlocuire a parametrilor echo "${var//,/$'\n'}")
  • De fapt, variabila conține adrese URL dintr-o formă zenity. Vreau să folosesc aceste adrese URL separat, așa că trebuie să obțin fiecare dintre ele în mod independent.
  • Relevant, dar nu o soluție bună în acest caz: Cum să atribuiți valoarea în timpul rulării în comanda AWK

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 portabil printf "$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 implicit name pentru read 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 ce bash se întâmplă să facă , dar acest lucru nu este standard. De asemenea, comanda read 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

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ă

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 și 3 î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 

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *