Próbuję, bez powodzenia, użyć polecenia awk w for pętla.
Mam zmienną zawierającą serię ciągów, które chcę wyciąć za pomocą awk aby uzyskać dane.
Wiem, jak to zrobić, ale naprawdę chcę wycinać dane sukcesywnie.
Mam więc zmienną:
var="data1,data2,data3"
I tutaj jestem teraz:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Próbuję zastąpić $1 przez pętlę $i ale bez powodzenia.
Komentarze
Odpowiedź
Możesz osiągnąć to, co „re próbując to zrobić, używając podwójnych cudzysłowów w skrypcie awk, aby wstrzyknąć do niego zmienną powłoki. Nadal chcesz zachować w nim jeden literał $, co możesz zrobić, zapisując go ukośnikiem odwrotnym :
echo $(awk -F, "{print \$$i}" <<<$var)
Spowoduje to rozwinięcie $i do 1, 2 i 3 w każdej z iteracji, dlatego awk zobaczy $1, $2 i $3, co spowoduje rozszerzenie każdego z pól.
Inną możliwością jest wprowadzenie zmiennej powłoki jako awk z użyciem flagi -v :
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
To przypisuje zmienną awk i do zawartości zmiennej powłoki o tej samej nazwie. Zmienne w awk nie używają elementu $, który jest używany w polach, więc $i wystarczy, aby odwołać się do i -te pole, jeśli i jest zmienną w awk.
Przypisanie zmiennej awk za pomocą -v jest ogólnie bezpieczniejsze podejście, szczególnie gdy może zawierać dowolne sekwencje znaków, w takim przypadku istnieje mniejsze ryzyko, że zawartość zostanie wykonana jako kod awk wbrew twoim zamiarom. Ale ponieważ w twoim przypadku zmienna zawiera jedną liczbę całkowitą, nie ma to większego znaczenia.
Jeszcze inną opcją jest użycie pętli for w samym awk . Zapoznaj się z dokumentacją awk (lub przeszukaj tę witrynę), aby uzyskać więcej informacji o tym, jak to zrobić.
Komentarze
- … lub ustaw rekord wejściowy separator odpowiednio
awk -v RS='[,\n]' 1 <<< "$var"(a może bardziej przenośnyprintf "$var" | awk -v RS=, 1) - @steeldriver Ten skrypt awk wypisze wszystkie trzy pola w Co jest w porządku, biorąc pod uwagę, że OP robi tylko to … Skoncentrowałem się w odpowiedzi na wyodrębnieniu pojedynczego pola przy założeniu, że byli zainteresowani wykonaniem innych poleceń w pętli powłoki (co może były motywacją do użycia jednego w pierwszej kolejności …)
- Zrozumiałem – że ' dlaczego skomentowałem PO, aby wyjaśnić, co naprawdę chcę zrobić;)
- pierwszy to zastrzyk (i problem bezpieczeństwa), drugi o ne (using
-v) nie jest zastrzykiem. - Dzięki, Twoja odpowiedź rozwiązała mój problem! Pierwszy raz zadaję tutaj pytanie po latach czytania postów. Nie zawiodłem się. Taka wspaniała społeczność!
Odpowiedź
Używanie awk wydaje się nadmierna w tej sytuacji, a co powiesz na tr i pętlę while:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Dane wyjściowe:
data1 data2 data3
Komentarze
- Świetnie.
REPLYto domyślnynameargument dlareadwbudowanego polecenia powłoki. - Zauważ, że wymaga to, aby powłoka używała domyślnej zmiennej do umieszczania danych z
read, co zdarza się robićbash, ale to nie jest standard. Ponadto poleceniereadmoże modyfikować dane, jeśli zawiera odwrotne ukośniki, i może usuwać flankujące białe znaki z wartości. Odczytuje również wartości z osadzonymi znakami nowej linii jako wiele wartości. Dodatkowo potrzebujesz"$REPLY", aby powstrzymać powłokę przed dzieleniem wartości i wykonywaniem na niej rozwijania nazw plików.
Odpowiedź
awk może zaakceptować zarówno j (jako zmienna), jak i $j (jako indeks pola):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i w przykładzie„ confused ”awk którego użyć (powłoka lub jej własna zmienna – mają pierwszeństwo), ponieważ obie są określane prefiksem $.
uwaga
sh , który jest standardem dla „przenośnych” skryptów, nie obsługuje:
(( i=1; i<=3; i++; )) i konstrukcje <<< $var
Możesz również rozważyć użycie polecenia seq w for pętla dla dokładniejszej kontroli generowania sekwencji liczb, jeśli jest dostępna.
Komentarze
- Twoja pętla działałaby tylko wtedy, gdybyś miał trzy pliki o nazwie ,
2i3w bieżącym katalogu. Ponadto w powłoce używane jest rozwijanie zmiennych bez cytowania, co może mieć niepożądane konsekwencje, jeśli dane zawierają wzorce nazw plików (np.*).echomoże ponadto modyfikować dane, jeśli zawierają one ukośniki odwrotne.
Odpowiedź
#!/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
W tym miejscu używamy podstawień zmiennych, aby uzyskać każde kolejne pole danych rozdzielone przecinkami z wartości zmiennej var. Pierwsze przypisanie do data w pętli usunie wszystko z $var po pierwszym przecinku. Zmienna var jest następnie modyfikowana w taki sposób, że usuwany jest pierwszy bit do pierwszego przecinka.
Trwa to do "$var" = "$data" co oznacza, że nic więcej nie można zrobić z łańcuchem.
Ten sposób pozwoliłby nam obsłużyć ciągi danych oddzielone przecinkami, które zawierają osadzone znaki nowej linii:
var="line1 line2,data2,last bit goes here"
Przy powyższych wartościach w var powyższy skrypt wyświetli wynik
data = "line1 line2" data = "data2" data = "last bit goes here"
Brak dbałości o osadzone znaki nowej linii; Bardzo rzadko musisz zapętlić wywołania awk.
Zauważ, że awk z wielką przyjemnością odczytuje Twój ciąg jako zbiór pól rozdzielanych przecinkami i że jest w stanie je przeglądać w pętli:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Z var="data1,data2,data3", to wypisze
data1 data2 data3
Kolejne rozwiązanie powłoki, które wykorzystuje IFS zmienna do podzielenia wartości $var na bity, jednocześnie używając set -f do wyłączenia rozwijania nazw plików:
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'}")