Sınırlandırılmış bir dizeyi awk içinde bir diziye nasıl bölebilirim?


169

Boru sembolleri içerdiğinde dizeyi bölme | . Onları dizide olmak için bölmek istiyorum.

denedim

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

Hangi iyi çalışıyor. Benim dize gibi ise "12|23|11"onları nasıl bir diziye bölmek?


3
Çıktınızın dizi öğelerini ayırıcı olmadan birleştirdiğini unutmayın. Bunun yerine onların birbirinden ayrılmasını istiyorsanız OFS, aralarına virgül koyarak printbirbirinden bağımsız argümanlar olarak görünmesini sağlayın.
dubiousjim

Veya sed kullanabilirsiniz:echo "12:23:11" | sed "s/.*://"
slushy

@slushy: emriniz, askerin ihtiyacı olan şey değil. komutunuz ( echo "12:23:11" | sed "s/.*://") son ":" işaretine kadar her şeyi silip yalnızca "11" değerini koruyarak ... son numarayı almak için çalışır, ancak değiştirilmesi (okunması zor bir şekilde) 2. sayı, vb awk (ve awk bölünmüş) çok daha zarif ve okunabilir.
Olivier Dulac

tek bir karaktere cut
bölmeniz gerekirse

Yanıtlar:


274

Denedin mi:

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

2
@Mohamed Saligh, Solaris'te iseniz , dize uzunluğu verildiğinde / usr / xpg4 / bin / awk kullanmanız gerekir .
Dimitre Radoulov

5
'benim için çalışmıyor'. özellikle yankı değerleri ve bölünmüş '|' Tipo? Hepinize iyi şanslar.
shellter

1
Bazı sözdizimi açıklamalarıyla daha iyi.
Alston

2
Bu GNU awk'ta işe yaramaz, çünkü üçüncü argüman splitdüzenli ifade ve |kaçması gereken özel bir simgedir. Kullanımsplit($0, a, "\|")
WhiteWind

1
@WhiteWind: |Özel bir sembol olarak değil, bir char olarak görülen "sağlamak" için başka bir yol , onu koymaktır []: yani, split($0, a, "[|]") # Bazı durumlarda, özellikle bazı regexp varyantı olarak, \ \ | perl vs grep vs .. diğerleri?) olabilir "|" tam anlamıyla yorumlandı ve "\ |" tersi yerine regex ayırıcı olarak görülen ... ymmv
Olivier Dulac

119

Bir dizeyi içindeki bir diziye bölmek awkiçin şu işlevi kullanırız split():

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

Herhangi bir ayırıcı verilmezse, FSvarsayılan olarak boşluk kullanılır:

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

Bir ayırıcı verebiliriz, örneğin ::

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

Aşağıdakileri ayarlamak için eşdeğerdir FS:

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

Gawk'de ayırıcıyı normal ifade olarak da sağlayabilirsiniz:

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

Ve hatta dördüncü parametresini kullanarak sınırlayıcının her adımda ne olduğunu görün:

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

GNU awk'nin man sayfasını alıntılayalım :

split (dize, dizi [, fieldsep [, seps]])

Bölme dizisi parçalara ayrılmış fieldsep ve parçaları saklamak dizi ve ayırıcı dizeleri SEPS dizisi. İlk parça depolanır array[1], ikinci parça array[2]içeri girer vb. Üçüncü argümanın dize değeri olan fieldsep , dizenin nereye bölüneceğini açıklayan bir normal ifade ( FS'nin giriş kayıtlarının nereye bölüneceğini açıklayan bir normal ifade olabilir). Eğer fieldsep atlanırsa, değeri FS kullanılır. split()oluşturulan eleman sayısını döndürür. seps , aradaki ayırıcı dize gawkolan bir uzantıdırseps[i]array[i]ve array[i+1]. Eğer fieldseptek bir boşluktur, o zaman önde gelen boşluklar girer seps[0]ve sonradan boşluklar girer seps[n], burada n dönüş değeri split()(yani dizideki eleman sayısı).


sadece normal awk değil, gnu awk kullandığınızı belirtin (seps'de [] ayırıcıları saklamaz ve diğer sınırlamaları vardır)
Olivier Dulac

17

Lütfen daha spesifik ol! "Çalışmıyor" ile ne demek istiyorsun? Tam çıkışı (veya hata mesajını), işletim sisteminizi ve awk sürümünüzü gönderin:

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

Veya bölme kullanarak:

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

Düzenleme: Solaris'te 4000 alanı doğru bir şekilde işlemek için POSIX awk ( / usr / xpg4 / bin / awk ) kullanmanız gerekir.


for(i = 0veya for(i = 1?
PiotrNycz

i = 0, çünkü sonra ++ i kullanıyorum (i ++ değil).
Dimitre Radoulov

3
Tamam - bunu fark etmedim. Daha okunabilir olduğuna inanıyorum for (i = 1; i <= n; ++i)...
PiotrNycz

5

echo "..." | awk ...Gereksiz forkve execsistem çağrıları çağırdığı için çözümü sevmiyorum .

Dimitre'nin çözümünü biraz bükerek tercih ediyorum

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

Ya da biraz daha kısa bir versiyon:

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

Bu durumda çıktı kaydı bir araya getirilir ki bu gerçek bir durumdur, böylece yazdırılır.

Bu özel durumda stdinyeniden yönlendirme, bir iç değişken:

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

kullandım oldukça uzun bir süre ama bu dahili dize manipülasyonu ile yönetilebilir. İlk durumda, orijinal dize dahili sonlandırıcı tarafından bölünür. İkinci durumda, dizenin her zaman bir karakter ayırıcıyla ayrılmış rakam çiftleri içerdiği varsayılır.

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}

Her durumda sonuç şudur:

112312

Bence sonuç çıktı verilen örnek çıktı ne olursa olsun, awk dizi değişken referansları olması gerekiyordu. Ama son sonucunu vermek için gerçekten kolay bir bash davasını kaçırdın. T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston

@DanielListon Haklısın! Teşekkürler! bash
Sonunda

4

Aslında awk'Girdi alan ayırıcı Değişken' adında bir özelliği vardır bağlantı . Bu nasıl kullanılır. Gerçekten bir dizi değil, ancak dahili $ değişkenlerini kullanıyor. Basit bir dizeyi bölmek için daha kolaydır.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

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

çalışmalı.



1

Şaka? :)

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

Bu benim çıktım:

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

sonuçta çalışıyor sanırım ..


Bu ipin uzunluğu nedeniyle mi? beri, benim dize uzunluğu 4000. herhangi bir fikir
Mohamed Saligh

1

Bunun eski bir soru olduğunu biliyorum, ama belki de hile gibi biri düşündüm. Özellikle bu çözüm belirli sayıda öğe ile sınırlı olmadığından.

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

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

Çıktı şöyle olacaktır:

Item: 12
Item: 23
Item: 11
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.