Bagaimana cara memproses file teks multi kolom untuk mendapatkan file teks multi kolom lainnya?

17

Saya punya file teks:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj

Bagaimana saya bisa memprosesnya dan mendapatkan file 2 kolom seperti ini:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Atau file tiga kolom seperti ini:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jj

Saya lebih suka mendapatkan solusi awk tetapi solusi lain juga disambut.

Hanya seorang pembelajar
sumber

Jawaban:

1

Anda juga dapat melakukannya dengan satu permintaan GNU awk:

membentuk ulang

# Set awk to split input at whitespace characters and
# use tab as the output field separator 
BEGIN {
  RS="[ \t\n]+"
  OFS="\t"
}

# Print using OFS or ORS based on the element index
{
  printf "%s", $1 (NR%n == 0 ? ORS : OFS)
}

# Append a missing new-line when last row is not full
END { 
  if( NR%n != 0) 
    printf "\n"
}

Jalankan seperti ini:

awk -f reshape.awk n=2 infile

Atau sebagai one-liner:

awk -v n=2 'BEGIN { RS="[ \t\n]+"; OFS="\t" } { printf "%s", $1 (NR%n == 0 ? ORS : OFS) } END { if( NR%n != 0) printf "\n" }' infile

Keluaran:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Atau dengan n=3:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Thor
sumber
Bukankah ini digunakan $1sebagai format string printf?
Wildcard
@ Kartu Memori: Benar, lebih aman untuk digunakan "%s", .... Diperbarui
Thor
Terimakasih sudah mengkonfirmasi. :) Hal yang sama berlaku untuk awkperintah di jawaban Anda yang lain untuk pertanyaan ini.
Wildcard
20

Letakkan setiap bidang pada baris dan setelah kolom.

Setiap bidang pada satu baris

tr

tr -s ' ' '\n' < infile

grep

grep -o '[[:alnum:]]*' infile

sed

sed 's/\s\+/\n/g' infile

atau lebih portabel:

sed 's/\s\+/\
/g' infile

awk

awk '$1=$1' OFS='\n' infile

atau

awk -v OFS='\n' '$1=$1' infile

Berbentuk kolom

tempel

Untuk 2 kolom:

... | paste - -

Untuk 3 kolom:

... | paste - - -

dll.

sed

Untuk 2 kolom:

... | sed 'N; s/\n/\t/g'

Untuk 3 kolom:

... | sed 'N; N; s/\n/\t/g'

dll.

xargs

... | xargs -n number-of-desired-columns

Saat xargsdigunakan /bin/echountuk mencetak, berhati-hatilah bahwa data yang kelihatannya seperti opsi echoakan ditafsirkan demikian.

awk

... | awk '{ printf "%s", $0 (NR%n==0?ORS:OFS) }' n=number-of-desired-columns OFS='\t'

pr

... | pr -at -number-of-desired-columns

atau

... | pr -at -s$'\t' -number-of-desired-columns

kolom (dari paket autogen)

... | columns -c number-of-desired-columns

Output khas:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Thor
sumber
2
Membanting dunk. +1 Pak
Steven Penny
Bukankah seharusnya xargspanggilan saluran echoatau printf?
Wildcard
1
@ Kartu Memori: xargspanggilan /bin/echosecara default
Thor
1
Wow, saya tidak tahu! Itu bahkan ditentukan oleh POSIX . Terima kasih!
Wildcard
@ Kartu Memori: Mengirim data xargsyang sepertinya pilihan /bin/echomenyebabkan masalah ... Saya menambahkan peringatan.
Thor
9
$ sed -E 's/\s+/\n/g' ip.txt | paste - -
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

$ sed -E 's/\s+/\n/g' ip.txt | paste - - -
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Sundeep
sumber
9

Seperti yang ditunjukkan Wildcard, ini hanya akan berfungsi jika file Anda diformat dengan baik, karena tidak ada karakter khusus yang akan ditafsirkan oleh shell sebagai gumpalan dan Anda senang dengan aturan pemisahan kata standar. Jika ada pertanyaan tentang apakah file Anda akan "lulus" tes itu, jangan gunakan pendekatan ini.

Satu kemungkinan akan digunakan printfuntuk melakukannya seperti

printf '%s\t%s\n' $(cat your_file)

Itu akan melakukan pemisahan kata pada konten your_filedan akan memasangkannya dan mencetaknya dengan tab di antaranya. Anda bisa menggunakan lebih banyak %sstring format dalam printfuntuk memiliki kolom tambahan.

Eric Renouf
sumber
1
Itu tergantung pada file yang tidak mengandung karakter khusus. Jika memiliki, misalnya, tanda bintang (*), Anda akan mendapatkan hasil yang sangat tak terduga.
Wildcard
4
perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(ganti 4 dengan jumlah kolom)

Joao
sumber
4

rsUtilitas BSD (membentuk kembali):

$ rs 0 2
a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj
[Ctrl-D][Enter]
a    aa
aaa  b
bb   bbb
c    cc
ccc  d
dd   ddd
e    ee
eee  f
ff   fff
g    gg
ggg  h
hh   hhh
i    ii
iii  j
jj   jjj

0 2adalah baris dan kolom . Menentukan 0berarti "menghitung baris secara otomatis dari kolom".

Kaz
sumber
3

Pendekatan skrip python.

Ide dasar di sini adalah untuk meratakan semua kata dalam teks Anda menjadi satu daftar, dan kemudian mencetak baris baru setelah setiap item kedua (itu untuk pengelompokan ke dalam dua kolom). Jika Anda ingin 3 kolom, ubah index%2keindex%3

#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%2 == 0:
       print("\t".join(line))
       line = []

Output sampel:

$ python recolumnate.py < input.txt                                            
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Versi tiga kolom (seperti yang disebutkan di atas, hanya index%3 == 0diubah)

$ cat recolumnate.py                                                           
#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%3 == 0:
       print("\t".join(line))
       line = []

$ python recolumnate.py < input.txt                                            
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Sergiy Kolodyazhnyy
sumber