Jeg prøver, uten hell, å bruke en awk
-kommando inne i en for
loop.
Jeg har en variabel som inneholder en serie strenger som jeg vil kutte med awk
for å få tak i dataene.
Jeg vet hvordan jeg gjør det, men det jeg virkelig vil er å kutte dataene suksessivt.
Så jeg har denne variabelen:
var="data1,data2,data3"
Og her hvor jeg er akkurat nå:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Jeg prøver å erstatte $1
ved sløyfen $i
men uten hell.
Kommentarer
Svar
Du kan oppnå det du er prøver å gjøre ved å bruke doble anførselstegn i awk-skriptet for å injisere skallvariabelen i den. Du vil fortsatt ha en bokstavelig $
i den, noe du kan gjøre ved å unnslippe den med tilbakeslag :
echo $(awk -F, "{print \$$i}" <<<$var)
Dette utvider $i
til 1
, 2
og 3
i hver av iterasjonene, derfor vil awk se $1
, $2
og $3
som får den til å utvide hvert av feltene.
En annen mulighet er å injisere skallvariabelen som en awk-variabel ved hjelp av -v
-flagget :
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Som tildeler awk-variabelen i til innholdet i skallvariabelen med samme navn. Variabler i awk bruker ikke en $
, som brukes til felt, så $i
er nok til å referere til i -th felt hvis i er en variabel i awk.
Å tilordne en awk-variabel med -v
er generelt en tryggere tilnærming, spesielt når den kan inneholde vilkårlige sekvenser av tegn, i så fall er det mindre risiko for at innholdet blir utført som awk-kode mot dine intensjoner. Men siden variabelen i ditt tilfelle har et enkelt heltall, er det mindre bekymringsfullt.
Nok et annet alternativ er å bruke en for
loop i awk selv Se awk-dokumentasjonen (eller søk på dette nettstedet) for mer informasjon om hvordan du gjør det.
Kommentarer
- … eller angi inngangsposten separator passende
awk -v RS='[,\n]' 1 <<< "$var"
(eller kanskje mer bærbartprintf "$var" | awk -v RS=, 1
) - @steeldriver Det awk-skriptet vil skrive ut alle tre feltene på Som er ok gitt OP gjør bare det … Jeg endte opp med å fokusere svaret på å trekke ut et enkelt felt under antagelse om at de var interessert i å utføre andre kommandoer i skallsløyfen (som kanskje har vært motivasjonen for å bruke en i utgangspunktet …)
- Forstått – at ‘ hvorfor jeg kommenterte OP for å avklare hva de virkelig vil gjøre;)
- Den første er en injeksjon (og et sikkerhetsproblem), den andre o ne (ved hjelp av
-v
) er ikke en injeksjon. - Takk, svaret ditt løste problemet mitt! Første gang jeg legger ut et spørsmål her etter år med å lese innlegg. Ikke skuffet. Et så flott samfunn!
Svar
Bruk av awk
virker overdreven i denne omstendigheten, hva med en tr
og en while-loop:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Output:
data1 data2 data3
Kommentarer
- Flott.
REPLY
som standardname
argument forread
innebygd skallkommando. - Merk at dette krever at skallet bruker en standardvariabel for å sette dataene inn fra
read
, sombash
tilfeldigvis gjør , men dette er ikke standard. Kommandoen dinread
kan også endre data hvis den inneholder tilbakeslag, og kan fjerne flankerende hvitt mellomrom fra verdiene. Det vil også lese verdier med innebygde nye linjer som flere verdier. Du trenger i tillegg"$REPLY"
for å stoppe skallet fra å dele verdien og fra å utføre filnavnutvidelse på den.
Svar
awk kan godta 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 som skal brukes (skall eller egen variabel – med forrang) som begge er referert til med $
prefiks.
note
sh shell som er standard for «bærbar» skripting støtter ikke:
(( i=1; i<=3; i++; ))
og <<< $var
konstruksjoner
Du kan også vurdere å bruke seq
kommando i for
loop for finere kontroll i nummersekvensgenerering, hvis tilgjengelig.
Kommentarer
- Sløyfen din vil bare fungere hvis du tilfeldigvis har tre filer kalt ,
2
og3
i gjeldende katalog. Du bruker også variabel utvidelse som ikke er sitert i skallet, noe som kan ha uønskede konsekvenser hvis dataene inneholder filnavnmønstre (som*
).echo
kan videre endre dataene hvis de inneholder tilbakeslag.
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 bruker vi variable erstatninger for å få hvert påfølgende komma-avgrenset datafelt fra verdien til var
-variabelen. Den første tildelingen til data
i løkken vil fjerne alt fra $var
etter det første kommaet. var
-variabelen blir deretter modifisert slik at den første biten opp til det første kommaet blir slettet.
Dette fortsetter til "$var" = "$data"
noe som betyr at ingenting mer kan gjøres mot strengen.
Denne måten å gjøre det på vil gjøre det mulig for oss å håndtere kommaadskilte datastrenger som inneholder innebygde nye linjer:
var="line1 line2,data2,last bit goes here"
Med de ovennevnte verdiene i var
, vil ovennevnte skript sende ut
data = "line1 line2" data = "data2" data = "last bit goes here"
Bryr meg ikke om innebygde nye linjer; Du må sjelden løpe over påkallinger av awk
.
Merk at awk
er veldig glad for å lese strengen som et sett med kommaavgrensede felt, og at den kan løpe over disse:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Med var="data1,data2,data3"
, dette vil skrive ut
data1 data2 data3
En annen skallløsning som bruker IFS
variabel for å dele $var
verdien i biter mens du også bruker set -f
for å deaktivere filnavnutvidelse:
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'}"
)