Awk-kommando i en for loop

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

  • Hvert tegn mellom et par enkelt anførselstegn blir behandlet som en bokstavelig karakter.
  • Hva er din virkelige brukssak? looping over awk virker unødvendig her (for eksempel i bash kan du bruke en parametererstatning echo "${var//,/$'\n'}")
  • Egentlig inneholder variabelviljen nettadresser fra en zenity-form. Jeg vil bruke disse urlene hver for seg, så jeg må skaffe hver enkelt av dem uavhengig.
  • Relevant, men ikke en god løsning i dette tilfellet: Hvordan tilordne verdi på kjøretid i AWK-kommando

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ærbart printf "$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 standard name argument for read innebygd skallkommando.
  • Merk at dette krever at skallet bruker en standardvariabel for å sette dataene inn fra read, som bash tilfeldigvis gjør , men dette er ikke standard. Kommandoen din read 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

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

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 og 3 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 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *