Awk-kommando inuti en för loop

Jag försöker, utan framgång, att använda ett awk -kommando inuti ett for loop.

Jag har en variabel som innehåller en serie strängar som jag vill klippa med awk för att få data.

Jag vet hur man gör det men vad jag verkligen vill är att klippa data successivt.

Så jag har den här variabeln:

var="data1,data2,data3" 

Och här där jag är just nu:

for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done 

Jag försöker ersätta $1 av slingan $i men utan framgång.

Kommentarer

  • Varje karaktär mellan ett par enskilda citat behandlas som en bokstavlig karaktär.
  • Vad är ditt verkliga användningsfall? looping över awk verkar onödigt här (för exaple in bash kan du använda en parameterbyte echo "${var//,/$'\n'}")
  • Egentligen innehåller variabeln webbadresser från en zenitetsform. Jag vill använda dessa webbadresser separat så jag måste skaffa var och en av dem oberoende.
  • Relevant men inte en bra lösning i det här fallet: Hur tilldela värde vid körning i AWK-kommandot

Svar

Du kan åstadkomma det du är försöker göra genom att använda dubbla citat i awk-skriptet för att injicera skalvariabeln i den. Du vill fortfarande behålla en bokstavlig $ i den, vilket du kan göra genom att undkomma den med backslash :

echo $(awk -F, "{print \$$i}" <<<$var) 

Detta utvidgar $i till 1, 2 och 3 i var och en av iterationerna, därför kommer awk att se $1, $2 och $3 vilket gör att den utvidgar vart och ett av fälten.

En annan möjlighet är att injicera skalvariabeln som en awk-variabel med flaggan -v :

echo $(awk -F, -v i="$i" "{print $i}" <<<$var) 

Det tilldelar awk-variabeln i till innehållet i skalvariabeln med samma namn. Variabler i awk använder inte en $, som används för fält, så $i räcker för att hänvisa till i -th fält om i är en variabel i awk.

Att tilldela en awk-variabel med -v är i allmänhet säkrare tillvägagångssätt, särskilt när den kan innehålla godtyckliga sekvenser av tecken, i så fall är det mindre risk att innehållet kommer att köras som awk-kod mot dina avsikter. Men eftersom variabeln i ditt fall innehåller ett enda heltal, är det mindre angeläget.

Ännu ett alternativ är att använda en for loop i awk själv Se awk-dokumentationen (eller sök på den här webbplatsen) för mer information om hur du gör det.

Kommentarer

  • … eller ställ in ingångsposten separator på lämpligt sätt awk -v RS='[,\n]' 1 <<< "$var" (eller kanske mer portabelt printf "$var" | awk -v RS=, 1)
  • @steeldriver Det awk-skriptet skriver ut alla tre fälten vid vilket är ok med tanke på att OP gör just det … Jag slutade med att fokusera svaret på att extrahera ett enda fält med antagandet att de var intresserade av att utföra andra kommandon i skalslingan (som kan har varit motivationen för att använda en i första hand …)
  • Förstått – att ’ varför jag kommenterade OP för att klargöra vad de verkligen vill göra;)
  • Den första är en injektion (och ett säkerhetsproblem), den andra o ne (med -v) är inte en injektion.
  • Tack, ditt svar löste mitt problem! Första gången jag lägger upp en fråga här efter år av att ha läst inlägg. Inte besviken. En sådan fantastisk gemenskap!

Svar

Att använda awk verkar överdriven under denna omständighet, vad sägs om en tr och en while-loop:

tr , "\n" <<<"$var" | while read; do echo $REPLY done 

Output:

data1 data2 data3 

Kommentarer

  • Bra. REPLY som standard name argument för read inbyggt skalkommando.
  • Observera att detta kräver att skalet använder en standardvariabel för att lägga in data från read, vilket bash råkar göra , men detta är inte standard. Ditt read -kommando kan också ändra informationen om det innehåller snedstreck och kan ta bort flankerande vitt utrymme från värdena. Det skulle också läsa värden med inbäddade nya rader som flera värden. Du behöver dessutom "$REPLY" för att stoppa skalet från att dela upp värdet och utföra filnamnsexpansion på det.

Svar

kan acceptera både j (som variabel) och $j (som fältindex):

 for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done  

$i i exemplet” förvirrad ”awk vilken som ska användas (skal eller en egen variabel – med företräde) som båda hänvisas till med $ prefix.

not

skal som är standard för ”portabelt” skript stöder inte:

(( i=1; i<=3; i++; )) och <<< $var konstruktioner

Du kan också överväga att använda seq kommando i for loop för finare kontroll vid generering av nummersekvenser, om tillgänglig.

Kommentarer

  • Din loop fungerar bara om du råkar ha tre filer som heter , 2 och 3 i den aktuella katalogen. Du använder också variabel expansion som inte är citerad i skalet, vilket kan få oönskade konsekvenser om data innehåller filnamnmönster (som *). echo kan dessutom modifiera data om de innehåller snedstreck.

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 

Här använder vi variabla substitutioner för att få varje successivt kommadelat avgränsat datafält från värdet för var -variabeln. Den första tilldelningen till data i slingan tar bort allt från $var efter det första komma. Variabeln var modifieras sedan så att den första biten upp till första komma tas bort.

Detta fortsätter tills "$var" = "$data" vilket innebär att ingenting mer kan göras mot strängen.

Detta sätt att göra det skulle göra det möjligt för oss att hantera kommaseparerade datasträngar som innehåller inbäddade nya rader:

var="line1 line2,data2,last bit goes here" 

Med ovanstående värden i var skulle ovanstående skript mata ut

data = "line1 line2" data = "data2" data = "last bit goes here" 

Att inte bry sig om inbäddade nya rader; Du väldigt sällan måste slingra på anrop av awk.

Observera att awk är helt glad att läsa din sträng som en uppsättning kommaseparerade fält och att den kan slinga över dessa:

printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }" 

Med var="data1,data2,data3", detta skulle skriva ut

data1 data2 data3 

En annan skallösning som använder IFS -variabel för att dela upp $var -värdet i bitar samtidigt som du använder set -f för att inaktivera filnamnsexpansion:

set -f oldIFS=$IFS; IFS="," set -- $var IFS=$oldIFS; unset oldIFS set +f for data do printf "data = "%s"\n" "$data" done 

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *