Saya memiliki file input dengan beberapa bagian yang ditandai dengan tag awal dan akhir, misalnya:
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
Saya ingin menerapkan transformasi ke file ini sehingga baris X, Y, Z difilter melalui beberapa perintah ( nl
, misalnya), tetapi sisa baris melewati tidak berubah. Perhatikan bahwa nl
(garis bilangan) mengakumulasi keadaan lintas garis, jadi ini bukan transformasi statis yang diterapkan pada masing-masing garis X, Y, Z. ( Sunting : ditunjukkan bahwa nl
dapat bekerja dalam mode yang tidak memerlukan status terakumulasi, tetapi saya hanya menggunakan nl
sebagai contoh untuk menyederhanakan pertanyaan. Pada kenyataannya perintah adalah skrip khusus yang lebih kompleks. Apa yang benar-benar saya cari adalah solusi umum untuk masalah penerapan filter standar ke subbagian file input )
Outputnya akan terlihat seperti:
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
Mungkin ada beberapa bagian dalam file yang memerlukan transformasi.
Pembaruan 2 Saya awalnya tidak menentukan apa yang akan terjadi jika ada lebih banyak satu bagian, misalnya:
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@inline-code-start
line L
line M
line N
@@inline-code-end
Harapan saya adalah bahwa negara hanya perlu dipertahankan dalam bagian tertentu, memberikan:
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line L
2 line M
3 line N
tetapi, saya pikir menafsirkan masalah sebagai mengharuskan negara untuk dijaga lintas bagian adalah sah, dan berguna dalam banyak konteks.
Akhiri Pembaruan 2
Pikiran pertama saya adalah membuat mesin keadaan sederhana yang melacak bagian apa yang kita hadapi:
#!/usr/bin/bash
while read line
do
if [[ $line == @@inline-code-start* ]]
then
active=true
elif [[ $line == @@inline-code-end* ]]
then
active=false
elif [[ $active = true ]]
then
# pipe
echo $line | nl
else
# output
echo $line
fi
done
Yang saya jalankan dengan:
cat test-inline-codify | ./inline-codify
Ini tidak berfungsi karena setiap panggilan ke nl
independen, sehingga nomor baris tidak bertambah:
line A
line B
1 line X
1 line Y
1 line Z
line C
line D
Upaya saya berikutnya adalah menggunakan fifo:
#!/usr/bin/bash
mkfifo myfifo
nl < myfifo &
while read line
do
if [[ $line == @@inline-code-start* ]]
then
active=true
elif [[ $line == @@inline-code-end* ]]
then
active=false
elif [[ $active = true ]]
then
# pipe
echo $line > myfifo
else
# output
echo $line
fi
done
rm myfifo
Ini memberikan output yang benar, tetapi dalam urutan yang salah:
line A
line B
line C
line D
1 line 1
2 line 2
3 line 3
Mungkin ada beberapa caching yang terjadi.
Apakah saya salah tentang semua ini? Ini sepertinya masalah yang cukup umum. Saya merasa harus ada saluran pipa sederhana yang akan menyelesaikan masalah ini.
sumber
nl
tidak harus mengakumulasi negara . Lihatnl -d
dan periksaman
/info
halaman untuk informasi tentangnl
's bagian pembatas .nl
sebagai filter contoh. Saya pikir itu akan menyederhanakan pertanyaan dengan mengoleskan rincian tentang apa sebenarnya yang dilakukan filter, tapi saya mungkin hanya menyebabkan lebih banyak kebingungan. Bahkan, saya memfilter subbagian melalui stabilo kode, untuk generator blog statis yang dikembangkan sendiri. Saat ini saya menggunakan gnusource-highlight
, tetapi itu mungkin berubah, dan saya mungkin menambahkan lebih banyak filter, seperti formatter juga.Jawaban:
Saya setuju dengan Anda - mungkin ini masalah umum. Namun, beberapa utilitas umum memiliki beberapa fasilitas untuk menanganinya.
nl
nl
, misalnya, memisahkan input menjadi halaman logis seperti-d
dihilangkan oleh pembatas bagian dua karakter . Tiga kemunculan pada satu garis saja mengindikasikan awal dari sebuah heading , dua body dan satu footer . Ini menggantikan semua yang ditemukan dalam input dengan garis kosong dalam output - yang merupakan satu-satunya baris kosong yang pernah dicetakSaya mengubah contoh Anda untuk memasukkan bagian lain dan memasukkannya ke dalam
./infile
. Jadi sepertinya ini:Kemudian saya menjalankan yang berikut:
nl
dapat dikatakan mengakumulasi keadaan di seluruh halaman logis, tetapi tidak secara default. Alih-alih itu akan memberi nomor baris inputnya sesuai dengan gaya , dan dengan bagian . Jadi-ha
berarti nomor semua baris tajuk dan-bn
berarti tidak ada garis tubuh - seperti yang dimulai dalam keadaan tubuh .Sampai aku belajar aku ini digunakan untuk menggunakan
nl
untuk masukan apapun, tapi setelah menyadari bahwanl
keluaran kekuatan mendistorsi menurut default-d
elimiter\:
saya belajar untuk lebih berhati-hati dengan itu dan mulai menggunakangrep -nF ''
untuk input belum teruji sebagai gantinya. Tapi pelajaran lain yang dipelajari hari itu adalah yangnl
bisa sangat berguna diterapkan dalam hal lain - seperti ini - jika Anda hanya memodifikasi inputnya hanya sedikit - seperti yang saya lakukan dengan dised
atas.KELUARAN
Inilah beberapa tentang
nl
- apakah Anda memperhatikan di atas bagaimana semua garis tetapi yang bernomor dimulai dengan spasi? Ketikanl
angka baris itu menyisipkan sejumlah karakter ke dalam kepala masing-masing. Untuk garis-garis itu tidak bernomor - bahkan kosong - selalu cocok dengan indent dengan memasukkan (-w
jumlah-s
idth + eparator len) * spasi di kepala baris yang tidak bernomor. Ini memungkinkan Anda mereproduksi konten yang tidak bernomor persis dengan membandingkannya dengan konten bernomor - dan dengan sedikit usaha. Ketika Anda mempertimbangkan bahwanl
akan membagi inputnya menjadi bagian-bagian logis untuk Anda, dan bahwa Anda dapat menyisipkan-s
tring sewenang-wenang di kepala setiap baris yang diberi nomor, maka itu akan cukup mudah untuk menangani outputnya:Cetakan di atas ...
GNU
sed
Jika
nl
bukan aplikasi target Anda, maka GNUsed
dapat melakukane
xecute perintah shell sewenang-wenang untuk Anda tergantung pada pertandingan.Di atas
sed
mengumpulkan input dalam ruang pola hingga cukup untuk berhasil melewati subtitusiT
est dan berhentib
peternakan kembali ke:l
abel. Ketika itu terjadi, itue
xecutesnl
dengan input diwakili sebagai<<
dokumen di sini untuk semua sisa-ruang pola.Alur kerjanya seperti ini:
/^@@.*start$/!b
^
seluruh baris$
tidak!
tidak/
cocok/
dengan pola di atas, makab
ranched dari script dan autoprinted - sehingga dari titik ini kita hanya bekerja dengan serangkaian garis yang dimulai dengan pola.s//nl <<\\@@/
s//
bidang kosong/
berarti alamat terakhir yangsed
dicoba cocok - jadi perintah ini menggantikan seluruh@@.*start
baris sebagainl <<\\@@
gantinya.:l;N
:
perintah mendefinisikan label cabang - di sini saya menetapkan satu nama:l
abel. TheN
perintah ext menambahkan baris berikutnya dari input ke ruang pola diikuti oleh\n
karakter ewline. Ini adalah salah satu dari hanya beberapa cara untuk mendapatkan\n
garis dised
ruang pola -\n
karakter garis adalah pembatas pasti untuksed
der yang telah melakukannya beberapa saat.s/\(\n@@\)[^\n]*end$/\1/
s///
ubstitution hanya dapat berhasil setelah start ditemui dan hanya pada kejadian pertama berikut sebuah akhir baris. Ini hanya akan bertindak pada ruang pola di mana garis akhir akhir\n
segera diikuti dengan@@.*end
menandai bagian paling akhir$
dari ruang pola. Ketika itu bertindak, itu menggantikan seluruh string yang cocok dengan grup\1
pertama , atau .\(
\)
\n@@
Tl
T
perintah est cabang untuk label (jika disediakan) jika substitusi yang berhasil belum terjadi sejak terakhir kali line input ditarik ke luar angkasa pola (seperti yang saya lakukan w /N
) . Ini berarti bahwa setiap kali\n
ewline ditambahkan ke ruang pola yang tidak cocok dengan pembatas akhir Anda,T
perintah est gagal dan bercabang kembali ke:l
abel, yang menghasilkansed
menarikN
garis ekst dan mengulang sampai berhasil.e
Ketika substitusi untuk pertandingan akhir berhasil dan skrip tidak bercabang kembali untuk
T
est gagal ,sed
akane
xecute perintah yang tampakl
seperti ini:Anda dapat melihatnya sendiri dengan mengedit baris terakhir yang ada agar terlihat seperti
Tl;l;e
.Mencetak:
while ... read
Salah satu cara terakhir untuk melakukan ini, dan mungkin cara yang paling sederhana, adalah menggunakan
while read
loop, tetapi untuk alasan yang bagus. Shell - (terutamabash
shell) - biasanya sangat buruk dalam menangani input dalam jumlah besar atau aliran stabil. Ini juga masuk akal - tugas shell adalah menangani input karakter demi karakter dan untuk memanggil perintah lain yang dapat menangani hal-hal yang lebih besar.Tetapi yang penting tentang perannya adalah bahwa shell tidak boleh
read
terlalu banyak dari input - itu ditentukan untuk tidak buffer input atau output ke titik yang mengkonsumsi begitu banyak atau tidak menyampaikan cukup pada waktunya sehingga perintah yang dipanggil tidak ada lagi - ke byte. Jadiread
dibuat untuk tes input yang sangat baik - untukreturn
informasi tentang apakah ada input yang tersisa dan Anda harus memanggil perintah berikutnya untuk membacanya - tetapi itu biasanya bukan cara terbaik untuk pergi.Berikut ini contoh, bagaimana seseorang dapat menggunakan
read
dan perintah lain untuk memproses input dalam sinkronisasi:Hal pertama yang terjadi untuk setiap iterasi adalah
read
menarik garis. Jika berhasil, ini berarti loop belum menekan EOF dan karenanya dalamcase
cocok dengan pembatas mulai ,do
blok segera dieksekusi. Lain,printf
cetak$line
ituread
dansed
dipanggil.sed
akanp
mematahkan setiap baris sampai bertemu dengan penanda awal - ketika iaq
menggunakan input sepenuhnya. The-u
beralih nbuffered diperlukan untuk GNUsed
karena bisa buffer agak rakus sebaliknya, tetapi - sesuai dengan spec - lain POSIXsed
s harus bekerja tanpa pertimbangan khusus - asalkan<infile
adalah file biasa.Ketika
sed
q
uits pertama , shell mengeksekusido
blok loop - yang memanggil orang lainsed
yang mencetak setiap baris sampai bertemu dengan penanda akhir . Ini pipa outputnya kepaste
, karena mencetak nomor baris masing-masing pada baris mereka sendiri. Seperti ini:paste
kemudian tempelkan bersama-sama pada:
karakter, dan seluruh output terlihat seperti:Ini hanya contoh - apa pun bisa dilakukan dalam tes atau melakukan blok di sini, tetapi utilitas pertama tidak boleh mengkonsumsi terlalu banyak input.
Semua utilitas yang terlibat membaca input yang sama - dan mencetak hasilnya - masing-masing pada gilirannya sendiri. Hal semacam ini bisa sulit untuk mendapatkan menguasainya - karena utilitas yang berbeda akan buffer lebih dari yang lain - tetapi umumnya Anda bisa mengandalkan
dd
,head
dansed
untuk melakukan hal yang benar (meskipun, untuk GNUsed
, Anda memerlukan cli-switch) dan Anda harus selalu dapat mengandalkanread
- karena itu, pada dasarnya, sangat lambat . Dan itulah mengapa loop di atas hanya menyebutnya satu kali per blok input.sumber
sed
contoh kedua yang Anda berikan, dan itu berhasil, tapi saya BENAR-BENAR mengalami kesulitan mengacak sintaks. (Sed saya cukup lemah dan biasanya terbatas pada s / findthis / replacethis / g. Saya harus berusaha untuk duduk dan benar-benar mengerti sed.)Satu kemungkinan adalah melakukan ini dengan editor teks vim. Itu dapat menyalurkan bagian yang sewenang-wenang melalui perintah shell.
Salah satu cara untuk melakukan ini adalah dengan nomor baris, menggunakan
:4,6!nl
. Perintah ex ini akan berjalan nl pada baris 4-6 inklusif, mencapai apa yang Anda inginkan pada input contoh Anda.Cara lain yang lebih interaktif adalah dengan memilih jalur yang sesuai menggunakan mode pemilihan garis (shift-V) dan tombol panah atau pencarian, lalu gunakan
:!nl
. Urutan perintah lengkap untuk input contoh Anda bisaIni tidak terlalu cocok untuk otomatisasi (jawaban menggunakan mis. Sed lebih baik untuk itu), tetapi untuk suntingan sekali pakai, sangat berguna tidak harus menggunakan skrip shell 20-baris.
Jika Anda tidak terbiasa dengan vi (m), setidaknya Anda harus tahu bahwa setelah perubahan ini Anda dapat menyimpan file menggunakan
:wq
.sumber
HOME=$(pwd) vim -c 'call Mf()' f
. Jika Anda menggunakan xargs, Anda mungkin ingin menggunakan gvim pada xserver khusus untuk mencegah kerusakan tty Anda (vnc adalah kartu video independen dan dapat dipantau).Perbaikan paling sederhana yang dapat saya pikirkan adalah tidak menggunakan
nl
tetapi menghitung sendiri garis-garisnya:Anda kemudian menjalankannya di file:
sumber
Jika tujuan Anda adalah mengirim seluruh blok kode ke satu contoh proses maka Anda dapat mengakumulasi baris dan menunda perpipaan hingga Anda mencapai akhir blok kode:
Ini menghasilkan yang berikut ini untuk file input yang mengulangi kasus uji tiga kali:
Untuk melakukan sesuatu yang lain dengan blok kode, misalnya mundur dan kemudian nomor, hanya pipa melalui sesuatu yang lain:
echo -E "${acc:1}" | tac | nl
. Hasil:Atau jumlah kata
echo -E "${acc:1}" | wc
:sumber
Edit menambahkan opsi untuk mendefinisikan filter yang disediakan pengguna
Secara default filter adalah "nl". Untuk mengubah opsi penggunaan filter "-p" dengan beberapa perintah yang disediakan pengguna:
atau
Filter terakhir ini akan menampilkan:
Pembaruan 1 Penggunaan IPC :: Open2 memiliki masalah penskalaan: jika buffersize terlampaui, mungkin diblokir. (di mesin saya pipa memperbesar jika 64K sesuai dengan 10_000 x "garis Y").
Jika kita membutuhkan hal-hal yang lebih besar (apakah kita membutuhkan lebih dari 10.000 "garis Y"):
(1) pasang dan gunakan
use Forks::Super 'open2';
(2) atau mengganti fungsi pipeit dengan:
sumber
$/
dans
flag), dan penggunaane
flag untuk melakukan panggilan aktual ke perintah eksternal. Saya sangat suka contoh kedua (ascii art)!/s
= ("." Berarti(.|\n)
);$/
mendefinisikan ulang pemisah register.Itu pekerjaan untuk awk.
Ketika skrip melihat penanda mulai, itu mencatat bahwa itu harus mulai disalurkan ke
nl
. Ketikapipe
variabel benar (bukan nol), output disalurkan kenl
perintah; ketika variabel salah (tidak disetel atau nol), output dicetak langsung. Perintah pipa adalah bercabang pertama kali membangun pipa ditemui untuk setiap string perintah. Evaluasi selanjutnya dari operator pipa dengan string yang sama menggunakan kembali pipa yang ada; nilai string yang berbeda akan membuat pipa yang berbeda. Theclose
Fungsi menutup pipa untuk string perintah yang diberikan.Ini pada dasarnya adalah logika yang sama dengan skrip shell Anda menggunakan pipa bernama, tetapi jauh lebih mudah untuk dieja, dan logika tutup dilakukan dengan benar. Anda harus menutup pipa pada waktu yang tepat, untuk membuat
nl
perintah keluar, menyiram buffernya. Script Anda sebenarnya menutup pipa terlalu dini: pipa ditutup segera setelahecho $line >myfifo
selesai pertama kali dijalankan. Namunnl
perintah hanya melihat akhir file jika mendapat waktu sebelum skrip dijalankanecho $line >myfifo
. Jika Anda memiliki volume data yang besar, atau jika Anda menambahkansleep 1
setelah menulismyfifo
, Anda akan melihatnyanl
hanya memproses baris pertama atau kumpulan cepat pertama, kemudian keluar karena terlihat di akhir inputnya.Dengan menggunakan struktur Anda, Anda harus membiarkan pipa terbuka sampai Anda tidak lagi membutuhkannya. Anda perlu memiliki pengalihan output tunggal ke dalam pipa.
(Saya juga mengambil kesempatan untuk menambahkan kutipan yang benar dan semacamnya - lihat Mengapa skrip shell saya tersedak di spasi putih atau karakter khusus lainnya? )
Jika Anda melakukan itu, Anda sebaiknya menggunakan pipa daripada pipa bernama.
sumber
do
. (Saya tidak punya perwakilan di sini untuk mengedit sedikit.)OK, pertama; Saya mengerti bahwa Anda tidak mencari cara untuk memberi nomor pada baris di bagian file Anda. Karena Anda belum memberikan contoh aktual tentang apa yang mungkin menjadi filter Anda (selain
nl
), anggaplah demikianyaitu, konversi teks ke semua huruf besar; jadi, untuk input
Anda menginginkan output dari
Inilah perkiraan pertama saya atas suatu solusi:
di mana spasi sebelum
@@
string, dan di dekat akhir baris terakhir, adalah tab. Harap dicatat bahwa saya menggunakannl
untuk tujuan saya sendiri . (Tentu saja saya melakukannya untuk menyelesaikan masalah Anda masalah , tetapi tidak untuk memberi Anda output nomor baris.)Ini memberi nomor pada garis-garis input sehingga kami dapat memecahnya di penanda bagian dan tahu cara menyusunnya kembali nanti. Bagian utama loop didasarkan pada upaya pertama Anda, dengan mempertimbangkan fakta bahwa penanda bagian memiliki nomor baris. Ini memecah input menjadi dua file:
file0
(tidak aktif; tidak di bagian) danfile1
(aktif; di bagian). Seperti inilah tampilan mereka untuk input di atas:Kemudian kita jalankan
file1
(yang merupakan gabungan dari semua baris dalam-bagian) melalui filter kapitalisasi; menggabungkannya dengan garis out-of-section tanpa filter; sortir, untuk mengembalikan mereka ke dalam urutan aslinya; dan kemudian menanggalkan nomor baris. Ini menghasilkan output yang ditunjukkan di dekat bagian atas jawaban saya.Ini mengasumsikan bahwa filter Anda meninggalkan nomor baris sendiri. Jika tidak (mis., Jika menyisipkan atau menghapus karakter di awal baris), maka, saya percaya, pendekatan umum ini masih dapat digunakan, tetapi akan memerlukan beberapa pengkodean yang sedikit lebih rumit.
sumber
nl
sudah melakukan sebagian besar pekerjaan di sana - itulah-d
pilihan untuk elimiter.Skrip shell yang menggunakan potongan sed untuk mengeluarkan garis-garis yang tidak dibatasi dan memberi makan potongan garis yang dibatasi ke dalam program filter:
Aku menulis naskah ini ke dalam sebuah file bernama detagger.sh dan menggunakannya sebagai begitu:
./detagger.sh infile.txt
. Saya membuat file filter.sh terpisah untuk meniru fungsi pemfilteran dalam pertanyaan:Tetapi operasi penyaringan dapat diubah dalam kode.
Saya mencoba mengikuti ide solusi generik dengan ini sehingga operasi seperti garis penomoran tidak memerlukan penghitungan tambahan / internal. Script melakukan beberapa pengecekan awal untuk melihat bahwa tag demarcator berpasangan dan tidak menangani tag bersarang sama sekali.
sumber
Terima kasih untuk semua ide bagus. Saya telah datang dengan solusi saya sendiri dengan melacak subbagian dalam file temp dan memipangnya sekaligus ke perintah eksternal saya. Ini sangat mirip dengan apa yang disarankan Supr (tetapi dengan variabel shell, bukan file temp). Juga, saya benar-benar menyukai ide menggunakan sed, tetapi sintaks untuk kasus ini tampaknya sedikit berlebihan bagi saya.
Solusi saya:
(Saya menggunakan
nl
hanya sebagai contoh filter)Saya lebih suka tidak harus berurusan dengan mengelola file temp, tapi saya mengerti bahwa variabel shell dapat memiliki batas ukuran yang agak rendah, dan saya tidak tahu adanya bash construct yang akan berfungsi seperti file temp, tetapi menghilang secara otomatis ketika proses berakhir.
sumber
M
,N
danO
akan diberi nomor4
,5
dan6
. Ini tidak melakukan itu. Jawaban saya memang (terlepas dari kenyataan bahwa, dalam inkarnasinya saat ini, itu tidak berfungsinl
sebagai filter). Jika ini jawaban adalah memberikan Anda output yang Anda inginkan, maka apa yang Anda maksud dengan “negara menumpuk di baris”? Apakah maksud Anda bahwa Anda ingin mempertahankan status hanya melalui setiap bagian, tetapi tidak di antara bagian (lintas)? (Mengapa Anda tidak memberikan contoh multi-bagian ke dalam pertanyaan Anda?)nl -p
untuk mendapatkanM,N,O==4,5,6
.