Awk-kommando inde i en for loop

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

  • Hver karakter imellem et par enkelt anførselstegn behandles som en bogstavelig karakter.
  • Hvad er din virkelige brugssag? looping over awk synes unødvendigt her (for exaple in bash kan du bruge en parameterudskiftning echo "${var//,/$'\n'}")
  • Faktisk indeholder variablen vil webadresser fra en zenity-form. Jeg vil bruge disse webadresser separat, så jeg bliver nødt til at få hver enkelt af dem uafhængigt.
  • Relevant, men ikke en god løsning i dette tilfælde: Sådan gør du tildele værdi ved kørselstid i AWK-kommando

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ærbart printf "$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 standard name argument for read indbygget shell-kommando.
  • Bemærk at dette kræver, at skallen bruger en standardvariabel til at placere dataene fra read, hvilket bash tilfældigvis gør , men dette er ikke standard. Din read 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

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

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

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *