Jak podzielić rozdzielany ciąg na tablicę w awk?

169

Jak podzielić ciąg, gdy zawiera on symbole potoku |. Chcę je podzielić, aby były w tablicy.

próbowałem

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Co działa dobrze. Jeśli mój ciąg jest taki, "12|23|11"jak podzielić je na tablicę?

Mohamed Saligh
źródło
3
Zwróć uwagę, że dane wyjściowe są konkatenacją elementów tablicy bez separatora. Jeśli zamiast tego chcesz, aby były one oddzielone OFS, wstaw przecinki między nimi, aby printzobaczyć je jako oddzielne argumenty.
dubiousjim
Lub możesz użyć sed:echo "12:23:11" | sed "s/.*://"
slushy
@slushy: Twoje polecenie nie jest tym, czego potrzebuje pytający. twoje polecenie ( echo "12:23:11" | sed "s/.*://") usuń wszystko do (włącznie) ostatniego ":", zachowując tylko "11" ... działa, aby uzyskać ostatnią liczbę, ale musiałby zostać zmodyfikowany (w trudny do odczytania sposób), aby uzyskać 2. numer, itd. awk (i podział awk) jest znacznie bardziej elegancki i czytelny.
Olivier Dulac,
jeśli chcesz podzielić na jedną postać, możesz użyćcut
ccpizza,

Odpowiedzi:

274

Czy próbowałeś:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'
Calin Paul Alexandru
źródło
2
@Mohamed Saligh, jeśli korzystasz z Solaris, musisz użyć / usr / xpg4 / bin / awk , biorąc pod uwagę długość łańcucha.
Dimitre Radoulov
5
„nie działa dla mnie”. szczególnie z dwukropkami między wyświetlanymi wartościami i podziałem ustawionym na podział na '|' ??? Literówka? Powodzenia wszystkim.
Shellter
1
Lepiej z wyjaśnieniem składni.
Alston
2
To nie zadziała w GNU awk, ponieważ trzeci argument do splitjest wyrażeniem regularnym i |jest specjalnym symbolem, który musi zostać zmieniony. Wykorzystaniesplit($0, a, "\|")
WhiteWind
1
@WhiteWind: innym sposobem „upewnienia się”, że |jest on postrzegany jako znak, a nie specjalny symbol, jest umieszczenie go między[] : tj. split($0, a, "[|]") # Podoba mi się to lepiej niż '\ |', w niektórych przypadkach, zwłaszcza gdy jest to jakiś wariant wyrażenia regularnego ( perl vs grep vs .. others?) może mieć "|" zinterpretowane dosłownie i „\ |” postrzegane jako separator wyrażeń regularnych, zamiast odwrotnie ... ymmv
Olivier Dulac
119

Aby podzielić ciąg na tablicę awk, używamy funkcji split():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

Jeśli nie podano separatora, używa znaku FS, który domyślnie jest spacją:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Możemy podać separator, na przykład ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Co jest równoważne ustawieniu go przez FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

W gawk możesz także podać separator jako wyrażenie regularne:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

A nawet zobacz, jaki separator znajdował się na każdym kroku, używając jego czwartego parametru:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Zacytujmy stronę podręcznika systemowego GNU awk :

split (string, array [, fieldsep [, seps]])

Podziel ciąg na części oddzielone sepami pól i zapisz je w tablicy, a ciągi separatorów w tablicy seps . Pierwsza część jest przechowywana array[1], druga część array[2]i tak dalej. Wartość ciągu trzeciego argumentu, sep-pól , jest wyrażeniem regularnym opisującym, gdzie podzielić łańcuch (podobnie jak FS może być wyrażeniem regularnym opisującym, gdzie podzielić rekordy wejściowe). Jeśli pominięto sep-pól , używana jest wartość FS . split()zwraca liczbę utworzonych elementów. seps to agawk rozszerzenie, które seps[i]jest łańcuchem oddzielającym pomiędzyarray[i]i array[i+1]. Jeśli sep-pól jest pojedynczą spacją, to wszelkie początkowe białe spacje trafiają do, seps[0]a wszystkie końcowe spacje przechodzą do seps[n], gdzie n jest wartością zwracaną split()(tj. Liczbą elementów w tablicy).

fedorqui 'SO przestań szkodzić'
źródło
wspomnij tylko, że używasz gnu awk, a nie zwykłego awk (który nie przechowuje separatorów w seps [] i ma inne ograniczenia)
Olivier Dulac,
17

Proszę być bardziej precyzyjnym! Co masz na myśli mówiąc „to nie działa”? Opublikuj dokładne dane wyjściowe (lub komunikat o błędzie), wersję swojego systemu operacyjnego i awk:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

Lub używając podziału:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Edycja: w Solarisie musisz użyć POSIX awk ( / usr / xpg4 / bin / awk ), aby poprawnie przetworzyć 4000 pól.

Dimitre Radoulov
źródło
for(i = 0czy for(i = 1?
PiotrNycz
i = 0, ponieważ używam ++ i po (nie i ++).
Dimitre Radoulov
3
Ok - tego nie zauważyłem. Jestem przekonany, że bardziej czytelne byłoby for (i = 1; i <= n; ++i)...
PiotrNycz
5

Nie podoba mi się to echo "..." | awk ...rozwiązanie, ponieważ nazywa niepotrzebne forki execsystemowe wywołania.

Wolę rozwiązanie Dimitre z odrobiną skrętu

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

Lub nieco krótsza wersja:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

W tym przypadku rekord wyjściowy złożony, co jest prawdziwym warunkiem, więc zostanie wydrukowany.

W tym konkretnym przypadku stdin przekierowania można oszczędzić, ustawiając plik zmienna wewnętrzna:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

użyłem dość długo, ale w można to zarządzać przez wewnętrzną manipulację ciągiem. W pierwszym przypadku oryginalny łańcuch jest dzielony przez wewnętrzny terminator. W drugim przypadku zakłada się, że łańcuch zawsze zawiera pary cyfr oddzielone separatorem jednoznakowym.

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

Wynik we wszystkich przypadkach to

112312
Prawda
źródło
Myślę, że wynikiem końcowym miały być odwołania do zmiennych tablicowych awk, niezależnie od podanego przykładu wydruku. Ale przegapiłeś naprawdę łatwy przypadek basha, aby zapewnić efekt końcowy. T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston
@DanielListon Masz rację! Dzięki! Nie wiedziałem, że końcowe / można pozostawić w tym bashwyrażeniu ...
TrueY
4

Faktycznie awkma funkcję o nazwie „Pole tekstowe Separator Zmienna” ogniwo . Oto jak go używać. W rzeczywistości nie jest to tablica, ale używa wewnętrznych zmiennych $. Aby rozdzielić prosty sznurek, jest to łatwiejsze.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'
Sven
źródło
3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

powinno działać.

codaddict
źródło
3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
Schildmeijer
źródło
1

Żart? :)

Co powiesz na echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Oto mój wynik:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

więc myślę, że to jednak działa ...

duedl0r
źródło
czy to z powodu długości łańcucha? ponieważ moja długość struny to 4000. wszelkie pomysły
Mohamed Saligh
1

Wiem, że to stare pytanie, ale pomyślałem, że może ktoś taki jak moja sztuczka. Zwłaszcza, że ​​to rozwiązanie nie ogranicza się do określonej ilości pozycji.

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

Wynik będzie:

Item: 12
Item: 23
Item: 11
Qorbani
źródło