Awk-commando in een for-lus

Ik probeer, zonder succes, een awk -commando te gebruiken in een for loop.

Ik “heb een variabele die een reeks strings bevat die ik wil knippen met awk om de gegevens te krijgen.

Ik weet hoe ik dat moet doen, maar wat ik echt wil, is de gegevens achtereenvolgens knippen.

Dus ik “heb deze variabele:

var="data1,data2,data3" 

En hier ben ik nu:

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

Ik probeer de $1 door de lus $i maar zonder succes.

Reacties

  • Elk teken tussen een paar enkele aanhalingstekens wordt als een letterlijk teken behandeld.
  • Wat is je echte gebruiksscenario? herhalen over awk lijkt hier niet nodig (bijvoorbeeld in bash zou je een parametervervanging kunnen gebruiken echo "${var//,/$'\n'}")
  • Eigenlijk bevat de variabele URLs van een zenity-vorm. Ik wil deze URLs afzonderlijk gebruiken, dus ik moet ze allemaal afzonderlijk ophalen.
  • Relevant, maar in dit geval geen goede oplossing: Hoe waarde toewijzen tijdens runtime in AWK-opdracht

Antwoord

Je kunt bereiken wat je doet proberen te doen door dubbele aanhalingstekens te gebruiken in het awk-script om de shell-variabele erin te injecteren. Je wilt er nog steeds één letterlijke $ in behouden, wat je kunt doen door eraan te ontsnappen met backslash :

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

Dit zal de $i uitbreiden naar 1, 2 en 3 in elk van de iteraties, daarom ziet awk $1, $2 en $3 waardoor het elk van de velden zal uitbreiden.

Een andere mogelijkheid is om de shell-variabele als een awk variabele met behulp van de -v vlag :

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

Dat wijst de awk-variabele i toe aan de inhoud van de shell-variabele met dezelfde naam. Variabelen in awk gebruiken geen $, die wordt gebruikt voor velden, dus $i is voldoende om naar de i -de veld als i een variabele in awk is.

Het toewijzen van een awk-variabele met -v is over het algemeen een veiliger aanpak, vooral als het willekeurige reeksen karakters kan bevatten, in dat geval is er minder kans dat de inhoud tegen uw bedoelingen als awk code wordt uitgevoerd. Maar aangezien in jouw geval de variabele een enkel geheel getal bevat, is dat minder zorgwekkend.

Nog een andere optie is om een for -lus in awk zelf te gebruiken . Zie awk documentatie (of zoek op deze site) voor meer details over hoe je dat moet doen.

Reacties

  • … of stel het invoerrecord in scheidingsteken op de juiste manier awk -v RS='[,\n]' 1 <<< "$var" (of misschien draagbaarder printf "$var" | awk -v RS=, 1)
  • @steeldriver Dat lastige script zal alle drie de velden afdrukken op wat ok is, aangezien het OP precies dat doet … Ik heb het antwoord uiteindelijk gericht op het extraheren van een enkel veld in de veronderstelling dat ze geïnteresseerd waren in het uitvoeren van andere commandos in de shell-lus (wat misschien zijn de motivatie geweest om er in de eerste plaats een te gebruiken …)
  • Begrepen – dat ‘ de reden is waarom ik commentaar gaf op het OP om te verduidelijken wat ze echt willen doen;)
  • De eerste is een injectie (en een beveiligingsprobleem), de tweede o ne (met -v) is geen injectie.
  • Bedankt, je antwoord loste mijn probleem op! De eerste keer dat ik hier een vraag stel na jarenlang berichten te hebben gelezen. Niet teleurgesteld. Wat een geweldige community!

Antwoord

Het gebruik van awk lijkt overdreven in deze omstandigheid, wat dacht je van een tr en een while-loop:

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

Output:

data1 data2 data3 

Reacties

  • Geweldig. REPLY is het standaard name argument voor read ingebouwde shell-opdracht.
  • Merk op dat dit vereist dat de shell een standaardvariabele gebruikt om de gegevens van read in te voeren, wat bash gebeurt , maar dit is niet standaard. Ook kan uw read -commando de gegevens wijzigen als het backslashes bevat, en flankerende witruimte van de waarden verwijderen. Het leest ook waarden met ingesloten nieuwe regels als meerdere waarden. Je hebt bovendien "$REPLY" nodig om te voorkomen dat de shell de waarde splitst en er geen bestandsnaam op uitbreidt.

Antwoord

kan accepteren zowel j (als variabele) en $j (als veldindex):

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

$i in het voorbeeld” confused “awk welke te gebruiken (shell of zijn eigen variabele – heeft voorrang) aangezien naar beide wordt verwezen met $ voorvoegsel.

note

shell die standaard is voor “draagbare” scripts ondersteunt niet:

(( i=1; i<=3; i++; )) en <<< $var constructies

Je zou ook kunnen overwegen om seq commando te gebruiken in for loop voor fijnere controle bij het genereren van nummerreeksen, indien beschikbaar.

Commentaren

  • Je loop zou alleen werken als je toevallig drie bestanden hebt aangeroepen , 2 en 3 in de huidige directory. U gebruikt ook variabele expansie zonder aanhalingstekens in de shell, wat ongewenste gevolgen kan hebben als de gegevens bestandsnaampatronen bevatten (zoals *). De echo kan de gegevens verder wijzigen als deze backslashes bevat.

Antwoord

#!/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 

Hier gebruiken we vervangende variabelen om elk opeenvolgend door kommas gescheiden gegevensveld uit de waarde van de var variabele te halen. De eerste toewijzing aan data in de lus zal alles verwijderen uit $var na de eerste komma. De var variabele wordt dan gewijzigd zodat het eerste bit tot aan de eerste komma wordt verwijderd.

Dit gaat door tot "$var" = "$data" wat betekent dat er niets meer aan de string kan worden gedaan.

Op deze manier zouden we door kommas gescheiden gegevensstrings kunnen verwerken die ingesloten nieuwe regels bevatten:

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

Met de bovenstaande waarden in var, zou het bovenstaande script

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

Geeft niet om ingesloten nieuwe regels; U hoeft zeer zelden aanroepen van awk te herhalen.

Merk op dat awk is heel blij om je string te lezen als een set van door kommas gescheiden velden, en dat het “in staat is om deze te doorlopen:

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

Met var="data1,data2,data3", dit zou afdrukken

data1 data2 data3 

Een andere shell-oplossing die gebruik maakt van de IFS variabele om de $var waarde in bits op te splitsen terwijl je ook set -f gebruikt om bestandsnaamuitbreiding uit te schakelen:

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

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *