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.
REPLY
to domyślnyname
argument dlaread
wbudowanego 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 polecenieread
moż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 ,
2
i3
w 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.*
).echo
moż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'}"
)