Manipulasi teks dengan sed

12

Saat ini, saya memiliki beberapa file teks dengan konten yang tampak seperti ini (dengan banyak baris):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Saya ingin mengubah setiap baris untuk memiliki format berikut:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Apakah ada cara melakukan hal di atas menggunakan sed? Atau apakah saya perlu menggunakan Python?

Zanna
sumber

Jawaban:

22

Anda bisa melakukannya dengan sed, ya, tetapi alat lain lebih sederhana. Sebagai contoh:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Penjelasan

awk akan membagi setiap baris masukan pada spasi (secara default), tabungan masing-masing bidang sebagai $1, $2, $N. Begitu:

  • printf "%s ", $2; akan mencetak bidang ke-2 dan spasi tambahan.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: akan beralih di atas bidang 3 ke bidang terakhir ( NFadalah jumlah bidang) dan untuk masing-masing bidang itu akan mencetak bidang ke-1, a: , kemudian bidang saat ini dan a :1.
  • print "" : ini hanya mencetak baris terakhir.

Atau Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Penjelasan

The -amerek perlberperilaku seperti awkdan membagi masukan pada spasi. Di sini, bidang disimpan dalam array @F, artinya bidang ke-1 akan $F[0], ke-2, $F[1]dll. Jadi:

  • print "$F[1] " : cetak bidang ke-2.
  • print "$F[0]:$_:1 " for @F[2..$#F];: iterate over field 3 ke bidang terakhir ( $#Fadalah jumlah elemen dalam array @F, jadi @F[2..$#F]ambil irisan array mulai dari elemen ke-3 sampai akhir array) dan cetak bidang 1, a :, lalu bidang saat ini dan :1.
  • print "\n" : ini hanya mencetak baris terakhir.
terdon
sumber
12

Ini dia mengerikan sed cara!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Lebih mudah dibaca:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

Catatan

  • -r gunakan ERE
  • s/old/new/ganti olddengannew
  • ^([0-9]+) simpan beberapa nomor di awal baris
  • \1 referensi kembali ke pola pertama yang disimpan
  • :a beri label pada bagian skrip ini a
  • ( |$) baik spasi atau ujung garis
  • t uji apakah penggantian terakhir berhasil - jika ya, maka lakukan perintah berikutnya
  • acari label :adan lakukan lagi
  • s/ $// hapus ruang trailing

Jadi setelah menambahkan struktur ke bagian pertama, kami berulang kali menemukan contoh terakhir dari struktur dan menerapkannya ke nomor berikutnya ...

Tapi saya setuju alat lain membuatnya lebih mudah ...

Zanna
sumber
Saya sedang menunggu solusi sed Anda: D
Ravexina
: D saya butuh waktu beberapa saat @Ravexina - Saya rasa muru dapat membuat yang lebih bersih
Zanna
5

Dengan awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

atau dengan bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

Keluaran:

0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
1 564: 7: 1 564: 12: 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 
Cyrus
sumber
5

Nah, Anda bisa melakukannya dengan sed, tetapi python juga berfungsi.

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Isi is reformatfile.pysebagai:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

Bagaimana cara kerjanya? Tidak ada yang istimewa yang terjadi. Kami membuka argumen baris perintah pertama sebagai file untuk dibaca dan melanjutkan dengan memecah setiap baris menjadi "kata" atau item individual. Kata pertama menjadi prefvariabel, dan kami mencetak item stdout kedua (kata [1]) yang diakhiri dengan spasi. Selanjutnya kita membangun set baru "kata-kata" melalui pemahaman daftar dan .join()fungsi pada daftar sementara pref, setiap kata, dan string "1". Langkah terakhir adalah mencetaknya

Sergiy Kolodyazhnyy
sumber
4

Dengan awk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

Ini semua tentang memformat bidang yang dipisahkan ruang dalam format yang diinginkan:

  • printf("%s ", $2) mencetak bidang kedua dengan spasi tambahan

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) beralih ke bidang terakhir ke 3 dan kedua dan mencetak bidang dalam format yang diinginkan (bidang pertama, lalu titik dua, lalu bidang saat ini, lalu titik dua, akhirnya 1) dengan spasi tambahan

  • printf("%s:%s:1\n", $1, $NF) mencetak bidang terakhir dengan baris baru

Contoh:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
heemayl
sumber