Misalkan ada beberapa teks dari file:
(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
Saya ingin menambahkan 11 ke setiap angka diikuti oleh "
di setiap baris jika ada satu, yaitu
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)
Ini solusi saya dengan menggunakan GNU AWK dan regex:
awk -F'#' 'NF>1{gsub(/"(\d+)\""/, "\1+11\"")}'
yaitu, saya ingin mengganti (\d+)\"
dengan \1+10\"
, di mana \1
grup mewakili (\d+)
. Tapi itu tidak berhasil. Bagaimana saya bisa membuatnya bekerja?
Jika gawk bukan solusi terbaik, apa lagi yang bisa digunakan?
Jawaban:
Coba ini (melongo diperlukan).
Uji dengan contoh Anda:
Perhatikan bahwa perintah ini tidak akan berfungsi jika dua angka (mis. 1 "dan" # 1 ") berbeda. Atau ada lebih banyak angka di baris yang sama dengan pola ini (mis. 23" ... 32 "..." # 123 ") dalam satu baris.
MEMPERBARUI
Karena @Tim (OP) mengatakan angka yang diikuti oleh
"
dalam baris yang sama bisa berbeda, saya melakukan beberapa perubahan pada solusi saya sebelumnya, dan membuatnya berfungsi untuk contoh baru Anda.BTW, dari contoh saya merasa itu bisa menjadi tabel struktur konten, jadi saya tidak melihat bagaimana dua angka itu bisa berbeda. Pertama adalah nomor halaman yang dicetak, dan yang kedua dengan # akan menjadi indeks halaman. Apakah saya benar?
Bagaimanapun, Anda tahu kebutuhan Anda yang terbaik. Sekarang solusi baru, masih dengan gawk (saya memecah perintah menjadi beberapa baris untuk membuatnya lebih mudah dibaca):
uji dengan contoh baru Anda :
EDIT2 berdasarkan komentar @Tim
Anda tepat untuk pemisah di bagian input dan output. Ini didefinisikan pemisah sebagai:
Ada dua tanda kutip ganda, karena lebih mudah untuk menangkap dua angka yang Anda inginkan (berdasarkan contoh input Anda).
Persis!
Ini dari http://www.gnu.org/s/gawk/manual/html_node/String-Functions.html . Anda dapat membaca untuk mendapatkan detail penggunaan gensub.
sumber
awk -F'#'
, tampaknya Anda hanya ingin melakukan perubahan pada bagian setelah '#'?FS=OFS="\" \"#"
pemisah bidang dalam input dan output adalah kuotasi ganda, spasi, kuotasi ganda, dan #? mengapa menentukan penawaran ganda dua kali? (2) dalam/.* ([0-9]+)$/
, apakah$
berarti akhir dari string? (3) dalam argumen ketiga gensub (), apa perbedaan antara"g"
dan"G"
?Tidak seperti hampir setiap alat yang menyediakan substitusi pengganti, awk tidak mengizinkan backreferensi seperti
\1
pada teks pengganti. GNU Awk memberikan akses ke grup yang cocok jika Anda menggunakanmatch
fungsi , tetapi tidak dengan~
atausub
ataugsub
.Perhatikan juga bahwa meskipun
\1
didukung, cuplikan Anda akan menambahkan string+11
, tidak melakukan perhitungan numerik. Juga, regexp Anda tidak benar, Anda mencocokkan hal-hal seperti"42""
dan tidak"#42"
.Inilah solusi awk (peringatan, belum diuji). Ini hanya melakukan penggantian tunggal per baris.
Akan lebih sederhana di Perl.
sumber
awk
dapat melakukannya, tetapi itu tidak langsung, bahkan menggunakan referensi ulang.GNU awk memiliki (sebagian) backreferecing, dalam bentuk gensub .
Contoh dari
123"
untuk sementara dibungkus\x01
dan\x02
untuk menandainya sebagai tidak dimodifikasi (untuksub()
. CoAtau Anda bisa melangkah melalui loop mengubah kandidat saat Anda pergi, dalam hal ini, referensi ulang dan "kurung" tidak diperlukan; tetapi melacak indeks karakter diperlukan.
Berikut ini cara lain, menggunakan
gensub
dan arraysplit
dan\x01
sebagai pembatas bidang (untuk split ) .. \ x02 menandai elemen array sebagai kandidat untuk penambahan aritmatika.sumber
"\x01\\1\"\x02"
artinya? Saya masih tidak mengerti\x01
dan\x02
. (2) seberapa berbedakah pengembalian$0
olehgensub
dan$0
sebagai argumen terakhirgensub
?\x01
dan\x02
digunakan sebagai penanda substitusi. Nilai-nilai ini sangat tidak mungkin ada dalam file teks normal , sehingga mereka sama-sama "sangat" aman untuk digunakan (mis. Tidak mengalami bentrok dengan yang sudah ada sebelumnya) .. Mereka hanya label sementara .. Re$0=gensub(... $0)
.. lihat ini Link String-Manipulation Functions , tetapi dalam ringkasan: Ini (gensub) mengembalikan string yang dimodifikasi sebagai hasil dari fungsi dan string target asli tidak diubah. ... Yang$0=
sederhana memodifikasi target semula ..Karena solusi dalam (g) awk tampaknya menjadi sangat kompleks, saya ingin menambahkan solusi alternatif di Perl:
Penjelasan:
-w
memungkinkan peringatan (yang akan memperingatkan Anda tentang kemungkinan efek yang tidak diinginkan).-p
menyiratkan loop di sekitar kode yang bekerja mirip dengan sed atau awk, menyimpan setiap baris input secara otomatis dalam variabel default$_
,.-e
memberi tahu perl bahwa kode program mengikuti pada baris perintah, bukan dalam file skrip.s/.../.../
) pada$_
, di mana urutan digit, jika diikuti oleh"
, akan diganti oleh urutan, ditafsirkan sebagai angka dalam penambahan, ditambah 11.(?=pattern)
penampilan untuk"
tanpa mengambil ke pertandingan, jadi kami tidak perlu mengulanginya di penggantian. Variabel MATCH$&
dalam penggantian kemudian hanya akan berisi angka./e
pengubah untuk regex memberitahuperl
untuk "mengeksekusi" penggantian sebagai kode bukannya mengambil sebagai string./g
pengubah membuat penggantian "global", mengulanginya pada setiap pertandingan di baris.Variabel MATCH
$&
sayangnya akan merusak kinerja kode dalam versi Perl sebelum 5.20. Solusi yang lebih cepat (dan tidak jauh lebih rumit) akan menggunakan pengelompokan dan backreference$1
sebagai gantinya:Dan jika pernyataan pandangan ke depan terlihat terlalu membingungkan, Anda juga dapat mengganti tanda kutip secara eksplisit:
sumber