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
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 draagbaarderprintf "$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 standaardname
argument voorread
ingebouwde shell-opdracht. - Merk op dat dit vereist dat de shell een standaardvariabele gebruikt om de gegevens van
read
in te voeren, watbash
gebeurt , maar dit is niet standaard. Ook kan uwread
-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
awk 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
sh 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
en3
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*
). Deecho
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
echo "${var//,/$'\n'}"
)