Jeg prøver uden succes at bruge en awk
kommando inde i en for
loop.
Jeg har en variabel, der indeholder en række strenge, som jeg vil klippe med awk
for at få dataene.
Jeg ved, hvordan man gør det, men hvad jeg virkelig vil have, er at klippe dataene successivt.
Så jeg har denne variabel:
var="data1,data2,data3"
Og her hvor jeg er lige nu:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Jeg prøver at erstatte $1
ved sløjfen $i
men uden succes.
Kommentarer
Svar
Du kan opnå det, du er forsøger at gøre ved at bruge dobbelt anførselstegn i awk-scriptet til at injicere shellvariablen i den. Du vil stadig have en bogstavelig $
i den, hvilket du kan gøre ved at undslippe den med tilbageslag :
echo $(awk -F, "{print \$$i}" <<<$var)
Dette udvider $i
til 1
, 2
og 3
i hver af iterationerne, derfor vil awk se $1
, $2
og $3
hvilket får det til at udvide hvert af felterne.
En anden mulighed er at indsprøjte shellvariablen som en awk-variabel ved hjælp af -v
flag :
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Det tildeler awk-variablen i til indholdet af shell-variablen med samme navn. Variabler i awk bruger ikke en $
, som bruges til felter, så $i
er nok til at henvise til i -th felt, hvis i er en variabel i awk.
Tildeling af en awk-variabel med -v
er generelt en mere sikker tilgang, især når den kan indeholde vilkårlige sekvenser af tegn, i så fald er der mindre risiko for, at indholdet udføres som awk-kode mod dine intentioner. Men da variablen i dit tilfælde indeholder et enkelt heltal, er det mindre vigtigt.
Endnu en mulighed er at bruge en for
loop i sig selv Se dokumentation for awk (eller søg på dette websted) for at få flere oplysninger om, hvordan du gør det.
Kommentarer
- … eller indstil input-posten separator passende
awk -v RS='[,\n]' 1 <<< "$var"
(eller måske mere bærbartprintf "$var" | awk -v RS=, 1
) - @steeldriver Det awk-script udskriver alle tre felter ved Hvilket er ok, da OP gør netop det … Jeg endte med at fokusere svaret på at udtrække et enkelt felt i den antagelse, at de var interesserede i at udføre andre kommandoer i shell-loop (som muligvis har været motivationen til at bruge en i første omgang …)
- Forstået – at ‘ hvorfor jeg kommenterede OPen for at præcisere, hvad de virkelig ønsker at gøre;)
- Den første er en indsprøjtning (og en sikkerhedsmæssig bekymring), den anden o ne (ved hjælp af
-v
) er ikke en indsprøjtning. - Tak, dit svar løste mit problem! Første gang jeg stiller et spørgsmål her efter mange års læsning af indlæg. Ikke skuffet. Sådan et godt samfund!
Svar
Brug af awk
virker overdreven under denne omstændighed, hvad med en tr
og en while-loop:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Output:
data1 data2 data3
Kommentarer
- Fantastisk.
REPLY
som standardname
argument forread
indbygget shell-kommando. - Bemærk at dette kræver, at skallen bruger en standardvariabel til at placere dataene fra
read
, hvilketbash
tilfældigvis gør , men dette er ikke standard. Dinread
kommando kan også ændre dataene, hvis de indeholder tilbageslag, og kan fjerne flankerende hvidt mellemrum fra værdierne. Det vil også læse værdier med indlejrede nye linjer som flere værdier. Du har desuden brug for"$REPLY"
for at stoppe shell fra at opdele værdien og udføre filnavnudvidelse på den.
Svar
awk kan acceptere både j
(som variabel) og $j
(som feltindeks):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
i eksemplet” forvirret “awk
hvilken der skal bruges (shell eller dens egen variabel – med forrang) som begge henvises til med $
præfikset.
note
sh shell, der er standard til “bærbar” scripting understøtter ikke:
(( i=1; i<=3; i++; ))
og <<< $var
konstruktioner
Du kan også overveje at bruge seq
kommando i for
loop til finere kontrol i nummersekvensgenerering, hvis tilgængelig.
Kommentarer
- Din loop fungerer kun, hvis du tilfældigvis har tre filer kaldet ,
2
og3
i den aktuelle bibliotek. Du bruger også variabel udvidelse, der ikke er citeret i skallen, hvilket kan have uønskede konsekvenser, hvis dataene indeholder filnavnemønstre (som*
).echo
kan desuden ændre data, hvis de indeholder tilbageslag.
Svar
#!/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
Her bruger vi variable erstatninger til at få hvert på hinanden følgende kommadelt område af værdien af var
-variablen. Den første tildeling til data
i sløjfen fjerner alt fra $var
efter det første komma. var
-variablen ændres derefter, så den første bit op til det første komma slettes.
Dette fortsætter, indtil "$var" = "$data"
hvilket betyder, at der ikke kan gøres mere med strengen.
Denne måde at gøre det på ville gøre det muligt for os at håndtere komma-adskilte datastrenge, der indeholder indlejrede nye linjer:
var="line1 line2,data2,last bit goes here"
Med ovenstående værdier i var
, vil ovenstående script output
data = "line1 line2" data = "data2" data = "last bit goes here"
Ligeglad med indlejrede nye linjer; Du er meget sjældent nødt til at løbe over påkaldelser af awk
.
Bemærk at awk
er meget glad for at læse din streng som et sæt kommaseparerede felter, og at den kan løbe over disse:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Med var="data1,data2,data3"
, dette vil udskrive
data1 data2 data3
En anden skalløsning, der bruger IFS
variabel for at opdele $var
værdi i bits, mens du også bruger set -f
til at deaktivere filnavnudvidelse:
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'}"
)