Ganti karakter kecuali x kejadian terakhir

9

Saya memiliki file yang memiliki banyak nama host yang berkorelasi dengan IP yang terlihat seperti ini:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

Saya ingin terlihat seperti ini:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

Bagaimana saya bisa mengganti. (titik) dari kolom pertama dengan - (tanda hubung) untuk memudahkan pengurutan berdasarkan kolom kedua? Saya berpikir untuk menggunakan sed untuk mengganti titik sampai spasi pertama, atau mengganti setiap titik kecuali tiga titik terakhir, tapi saya kesulitan memahami regex dan sed. Saya dapat melakukan penggantian sederhana tetapi ini jauh di atas kepala saya!

Ini adalah bagian dari skrip yang lebih besar yang telah saya tulis di bash. Saya terjebak di bagian ini.

Florin
sumber

Jawaban:

7

Anda bisa menggunakan AWK

awk '{gsub(/-/,".",$1);print}' infile

Penjelasan

awkmemisahkan garis pada spasi putih secara default. Dengan demikian, kolom pertama dari baris ( $1dalam awk-ese) akan menjadi yang Anda inginkan untuk melakukan pergantian. Untuk tujuan ini, Anda dapat menggunakan:

 gsub(regex,replacement,string)

untuk melakukan penggantian yang diperlukan.

Catatan yang gsubhanya didukung untuk gawkdan nawktetapi pada banyak distro modern awkadalah softlink ke gawk.

Rahul Patil
sumber
1
+1 Kalahkan saya untuk itu. Saya pikir penjelasan akan sangat bermanfaat bagi penanya dan pembaca masa depan juga.
Joseph R.
1
@ JosephRR. Maaf saya tidak pandai penjelasan tapi saya sudah mencoba dan memperbarui ..
Rahul Patil
2
awkBerdasarkan spesifikasi POSIX nawk, maka semua awkimplementasi modern seharusnya memilikinya gsub. Pada Solaris, Anda mungkin perlu /usr/xpg4/bin/awkatau nawk.
Stéphane Chazelas
@RahulPatil Jika Anda tidak keberatan, saya menambahkan beberapa baris yang saya pikir akan membantu orang lain.
Joseph R.
@JosephR terima kasih .., sepertinya sempurna sekarang .. :)
Rahul Patil
6

Jika Anda perlu melakukan pergantian pada bidang pertama, yang terbaik adalah menggunakan solusi awk Rahul tetapi berhati-hatilah karena itu dapat mempengaruhi spasi (bidang ditulis ulang dengan satu ruang di antaranya).

Anda dapat menghindarinya dengan menulisnya sebagai gantinya:

perl -pe 's|\S+|$&=~tr/./-/r|e' file

The -pberarti bendera "membaca file baris masukan demi baris dan mencetak setiap baris setelah menerapkan script yang diberikan oleh -e". Kemudian, gantikan ( s|pattern|replacement|) urutan pertama karakter non-spasi ( \S+) dengan pola yang cocok ( $&) setelah mengganti semua .dengan -. Caranya adalah dengan menggunakan di s|||emana eoperator akan mengevaluasi ekspresi sebagai pengganti. Jadi, Anda dapat memiliki satu pengganti ( tr/./-/) diterapkan pada kecocokan ( $&) dari yang sebelumnya ( s|||e).

Jika Anda perlu mengganti setiap .dengan -kecuali 3 yang terakhir, dengan GNU seddan dengan asumsi Anda memiliki revperintah:

rev file | sed 's/\./-/4g' | rev
Stéphane Chazelas
sumber
1
Perhatikan bahwa solusi Perl mengasumsikan versi 5.14 atau lebih tinggi (agar /rdapat bekerja).
Joseph R.
3

Sed bukan alat termudah untuk pekerjaan itu - lihat jawaban lain untuk alat yang lebih baik - tetapi itu bisa dilakukan.

Untuk mengganti .dengan -hanya sampai ke ruang pertama, penggunaan sdalam satu lingkaran.

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./\1-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(Perhatikan bahwa beberapa implementasi sed tidak mendukung komentar pada baris yang sama. GNU sed tidak.)

Untuk melakukan penggantian hingga ruang terakhir:

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-\1/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

Teknik lain memanfaatkan ruang pegang sed. Simpan bit yang tidak ingin Anda modifikasi ke dalam ruang tunggu, lakukan pekerjaan Anda, lalu ingat ruang penyimpanan. Di sini, saya membagi garis di ruang terakhir dan mengganti titik dengan tanda hubung di bagian pertama.

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'
Gilles 'SANGAT berhenti menjadi jahat'
sumber
2

Karena Rahul memberi Anda jawaban kanonik untuk kasus penggunaan Anda, saya pikir saya akan mencoba menjawab masalah tituler: mengganti semua kecuali kejadian x terakhir dari sebuah regex:

perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

Kode di atas (diuji) tidak menganggap bahwa Anda memiliki bidang yang dipisahkan oleh ruang. Ini akan mengganti semua titik pada garis dengan tanda hubung kecuali 3 titik terakhir. Ganti 3kode dalam sesuai keinginan Anda.

Joseph R.
sumber
2

Anda dapat menggunakan banyak alat berbeda untuk ini. Rahul Patil sudah memberi Anda gawksatu jadi di sini ada beberapa yang lain:

  • perl

    perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    The -apenyebab beralih perl untuk jalur input secara otomatis split pada spasi dan menyimpan bidang yang dihasilkan ke dalam array @F. Oleh karena itu, bidang pertama akan $F[0]jadi kami mengganti ( s///) semua kemunculan .dengan -di bidang pertama dan kemudian mencetak seluruh larik.

  • kulit

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    Di sini, loop sementara membaca file dan secara otomatis membelah pada spasi putih. Ini menciptakan dua bidang, $firstdan $rest. Konstruk ${first//pattern/replacement}menggantikan semua kejadian patterndengan replacement.

terdon
sumber
+1 Sementara perlrun(1)akan memberi tahu Anda bahwa -a"mode autosplit", saya lebih suka menganggapnya sebagai " awkmode": D
Joseph R.
2

Saya percaya ini sedikit lebih mudah dibaca daripada regex jahat besar. Pada dasarnya saya hanya membagi garis menjadi dua bidang di spasi dan menggunakan sed pada bagian pertama.

while read -r host ip; do
    echo "$(sed 's/\./-/g' <<< "$host") $ip"
done < input_file

Bergantung pada shell Anda, Anda juga bisa menggunakan $ {host //./-} alih-alih perintah sed.

maedox
sumber
0
sed 's/\./-/' <file name>

Tanpa menggunakan gdi akhir perintah Anda bisa melakukan ini ... Ini hanya akan menggantikan pola 1 kejadian

sunandan
sumber