Bagaimana cara membagi string yang dibatasi menjadi array di awk?

169

Cara membagi string ketika mengandung simbol pipa |di dalamnya. Saya ingin membaginya menjadi array.

Saya mencoba

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

Itu bekerja dengan baik. Jika string saya seperti "12|23|11"lalu bagaimana cara membaginya menjadi array?

Mohamed Saligh
sumber
3
Perhatikan bahwa output Anda menggabungkan elemen array, tanpa pemisah. Jika Anda ingin mereka dipisahkan dengan OFS, koma tetap di antara mereka, menjadikannya printsebagai argumen yang terpisah.
dubiousjim
Atau Anda dapat menggunakan sed:echo "12:23:11" | sed "s/.*://"
cair
@ Slushy: perintah Anda sama sekali tidak apa yang penanya butuhkan. perintah Anda ( echo "12:23:11" | sed "s/.*://") hapus semuanya sampai (dan termasuk) ":" terakhir, simpan hanya "11" ... itu berfungsi untuk mendapatkan nomor terakhir, tetapi perlu diubah (dengan cara yang sulit dibaca) untuk mendapatkan nomor 2, dll. awk (dan split awk) jauh lebih elegan dan mudah dibaca.
Olivier Dulac
jika Anda perlu membagi satu karakter yang dapat Anda gunakancut
ccpizza

Jawaban:

274

Sudahkah Anda mencoba:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'
Calin Paul Alexandru
sumber
2
@Mohamed Saligh, jika Anda menggunakan Solaris, Anda harus menggunakan / usr / xpg4 / bin / awk , mengingat panjang string.
Dimitre Radoulov
5
'tidak bekerja untuk saya'. terutama dengan titik dua antara nilai-nilai yang digaungkan dan pengaturan split untuk dibagi pada '|' ??? Salah ketik? Semoga beruntung untuk semua.
kerang
1
Lebih baik dengan beberapa penjelasan sintaks.
Alston
2
Ini tidak akan berfungsi di GNU awk, karena argumen ketiga splitadalah ekspresi reguler, dan |merupakan simbol khusus, yang harus diloloskan. Gunakansplit($0, a, "\|")
WhiteWind
1
@WhiteWind: cara lain untuk "memastikan" yang |dilihat sebagai char dan bukan simbol khusus adalah dengan meletakkannya di antara []: yaitu, split($0, a, "[|]") # Saya suka ini lebih baik daripada '\ |', dalam beberapa kasus, terutama karena beberapa varian regexp ( perl vs grep vs .. lainnya?) dapat memiliki "|" diinterpretasikan secara harfiah dan "\ |" dilihat sebagai pemisah regex, bukannya sebaliknya ... ymmv
Olivier Dulac
119

Untuk membagi string ke array di awkkita menggunakan fungsi split():

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

Jika tidak ada pemisah yang diberikan, ia menggunakan FS, yang default ke spasi:

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

Kami dapat memberikan pemisah, misalnya ::

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

Yang setara dengan mengaturnya melalui FS:

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

Di gawk Anda juga dapat menyediakan pemisah sebagai regexp:

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

Dan bahkan melihat apa pembatas pada setiap langkah dengan menggunakan parameter keempatnya:

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

Mari kutip halaman manual GNU awk :

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

Membagi string menjadi potongan-potongan yang dipisahkan oleh fieldsep dan menyimpan potongan-potongan dalam array dan string pemisah dalam array seps . Potongan pertama disimpan array[1], potongan kedua masuk array[2], dan sebagainya. Nilai string dari argumen ketiga, fieldsep , adalah regexp yang menggambarkan di mana untuk membagi string (sebanyak FS dapat menjadi regexp yang menggambarkan di mana untuk membagi catatan input). Jika fieldsep dihilangkan, nilai FS digunakan. split()mengembalikan jumlah elemen yang dibuat. September adalah gawkekstensi, dengan seps[i]menjadi string pemisah antaraarray[i]dan array[i+1]. Jika fieldsep adalah ruang tunggal, maka setiap spasi putih terkemuka masuk ke seps[0]dan spasi spasi tambahan apa pun masuk ke dalam seps[n], di mana n adalah nilai balik split()(yaitu, jumlah elemen dalam array).

fedorqui 'SO berhenti merugikan'
sumber
sebut saja Anda menggunakan gnu awk, bukan awk biasa (yang tidak menyimpan pemisah dalam September [], dan memiliki batasan lain)
Olivier Dulac
17

Harap lebih spesifik! Apa yang Anda maksud dengan "itu tidak berhasil"? Posting output yang tepat (atau pesan kesalahan), OS Anda dan versi awk:

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

Atau, menggunakan split:

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

Sunting: pada Solaris Anda harus menggunakan POSIX awk ( / usr / xpg4 / bin / awk ) untuk memproses 4000 bidang dengan benar.

Dimitre Radoulov
sumber
for(i = 0atau for(i = 1?
PiotrNycz
i = 0, karena saya menggunakan ++ i after (bukan i ++).
Dimitre Radoulov
3
Ok - saya tidak melihat ini. Saya sangat percaya akan lebih mudah dibaca for (i = 1; i <= n; ++i)...
PiotrNycz
5

Saya tidak suka echo "..." | awk ...solusinya karena panggilan tidak perlu forkdan execpanggilan sistem.

Saya lebih suka solusi Dimitre dengan sedikit twist

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

Atau versi yang sedikit lebih pendek:

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

Dalam hal ini catatan keluaran disatukan yang merupakan kondisi sebenarnya, sehingga dicetak.

Dalam kasus khusus ini stdinpengalihan dapat disimpan dengan pengaturan variabel internal:

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

Saya menggunakan cukup lama, tetapi di ini dapat dikelola dengan manipulasi string internal. Dalam kasus pertama string asli dibagi oleh terminator internal. Dalam kasus kedua diasumsikan bahwa string selalu berisi pasangan digit yang dipisahkan oleh pemisah satu karakter.

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}

Hasilnya dalam semua kasus adalah

112312
Benar
sumber
Saya pikir hasil akhirnya seharusnya menjadi referensi variabel array awk, terlepas dari contoh hasil cetak yang diberikan. Tetapi Anda melewatkan bash case yang sangat mudah untuk memberikan hasil akhir Anda. T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston
@DanielListon Anda benar! Terima kasih! Saya tidak tahu bahwa trailing / dapat dibiarkan dalam bashungkapan ini ...
Benar
4

Sebenarnya awkmemiliki fitur yang disebut tautan 'Input Field Separator Variable' . Inilah cara menggunakannya. Ini sebenarnya bukan array, tetapi menggunakan variabel $ internal. Untuk memisahkan string sederhana lebih mudah.

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

harus bekerja.

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

Lelucon? :)

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

Ini adalah output saya:

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

jadi saya kira itu berhasil setelah semua ..

duedl0r
sumber
Apakah itu karena panjangnya tali? karena, panjang tali saya adalah 4000. ada ide
Mohamed Saligh
1

Saya tahu ini adalah pertanyaan lama, tapi saya pikir mungkin seseorang menyukai tipuan saya. Terutama karena solusi ini tidak terbatas pada jumlah item tertentu.

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

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

Outputnya adalah:

Item: 12
Item: 23
Item: 11
Qorbani
sumber