Bagaimana cara memasukkan konten file ke file lain sebelum pola (penanda)?


32

File1 isi:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2 isi:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

Setelah eksekusi skrip perl / shell, File2konten harus menjadi:

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"

yaitu menempelkan konten File1di File2sebelum baris yang berisi "Pointer".


1
Juga ditanyakan di StackOverflow
glenn jackman


Saya sudah memilih pertanyaan SO untuk penutupan karena alasan ini, tidak ada lagi alasan untuk menutup pertanyaan ini. Btw, pertanyaannya adalah kualitas yang jauh lebih buruk pada SO, jadi logika menentukan untuk menutup itu dan bukan ini.
peterh mengatakan mengembalikan Monica

Jawaban:


33

sed memiliki fungsi untuk itu, dan dapat melakukan inline modifikasi:

sed -i -e '/Pointer/r file1' file2

Tapi ini menempatkan Pointer Anda di atas file1. Untuk meletakkannya di bawah ini, tunda keluaran saluran:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 

8
Bisakah Anda jelaskan apa yang -e 1x -e '2,${x;p}' -e '${x;p}'harus dilakukan? Saya mengerti bahwa Anda bertukar barang di buffer pola dan kemudian mencetaknya, tetapi saya tidak tahu apa dan mengapa Anda menambahkan opsi diam -ndi awal.
hdl

@ jfg956 Apakah mungkin untuk hanya mengganti dan menghapus bagian 'Pointer' dari file asli. Ini saya bisa mencari tahu dengan sed menyapu kedua, tetapi apakah mungkin untuk melakukannya dalam satu kali?
Alexander Cska

17

Tanpa menggunakan sedatau awk...

Pertama, temukan garis yang menjadi pola Anda:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

Kemudian, gunakan 3 perintah untuk menampilkan hasil yang diinginkan:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

Ini memiliki kelemahan mengakses 3 kali file tersebut file2, tapi mungkin lebih jelas daripada seddari awksolusi.


10

awkmembuat ini cukup mudah.
Masukkan baris sebelum file:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

Untuk membuat file dalam dicetak setelah Pointerbaris, cukup alihkan urutan pola (Anda perlu menambahkan tanda titik koma untuk mendapatkan tindakan default), dan Anda dapat menjatuhkan linevariabel:

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

Dan hanya karena belum ada yang menggunakan perl,

# insert file before line
perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile

ini bekerja, tetapi dihapus garis yang mengandung pointer
user1228191

demikian juga, bagaimana menempelkan isi file 1 dalam file 2 setelah itu "Pointer" yang mengandung baris menggunakan awk
user1228191

@ user1228191 Memperbaiki yang pertama, menambahkan yang kedua.
Kevin

Versi 'perl' tampaknya tidak berfungsi. system("cat innerfile")output innerfileke konsol. Apakah saya melewatkan sesuatu?
kaartic

perintah awk [gawk '/ <body> / {while (getline line <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] hanya loop dan mencetak jutaan baris . gawk V4.2.0 Apa yang saya lewatkan di sini?
JESii

7

Pekerjaan mudah untuk ed:

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1membaca dalam file yang ditentukan setelah baris yang dialamatkan, yang dalam hal ini adalah baris sebelum pencocokan baris pertama Pointer. Jadi ini akan menyisipkan konten file2hanya sekali bahkan jika Pointerterjadi pada beberapa baris. Jika Anda ingin menyisipkannya sebelum setiap baris yang cocok tambahkan gbendera lobal:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

Ganti ,pdengan wjika Anda ingin mengedit file di tempat.


sedJawaban yang diterima memang berfungsi untuk sebagian besar kasus tetapi jika marker ada di baris terakhir, perintah tidak akan berfungsi seperti yang diharapkan: ia akan memasukkan konten File1setelah marker.
Awalnya saya sudah mencoba dengan:

sed '/Pointer/{r file1
N}' file2

yang juga berfungsi dengan baik (seperti rakan melakukan keajaibannya di akhir siklus) tetapi memiliki masalah yang sama jika marker ada di baris terakhir (tidak ada Ngaris ext setelah baris terakhir). Untuk mengatasinya, Anda dapat menambahkan baris baru ke input Anda:

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)

Ini akan memasukkan file2konten sebelum setiap baris yang cocok. Untuk menyisipkannya hanya sebelum baris yang cocok pertama Anda bisa menggunakan loop dan cukup tarik ngaris ext sampai Anda mendapatkan akhir file:

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)

Dengan sedsolusi ini Anda kehilangan kemampuan untuk mengedit di tempat (tetapi Anda dapat mengarahkan ke file lain).


6

Gunakan loop untuk membaca baris dalam file2. Jika Anda menemukan garis yang dimulai dengan Pointer, maka cetak file1. Ini ditunjukkan di bawah ini:

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2

4

Ada beberapa cara untuk menyelesaikan ini dengan sed. Salah satu caranya adalah pembacaan yang tertunda sebagaimana direkomendasikan dalam jawaban yang diterima. Bisa juga ditulis seperti:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

... dengan sedikit melihat-depan secara eksplisit alih-alih melihat-diimplementasikan di tempat lain dengan buffer penahan. Yang pasti akan memiliki masalah yang sama dengan baris terakhir yang catatan @don_crissti, meskipun, karena N tidak kenaikan siklus baris dan rperintah ead diterapkan oleh nomor baris.

Anda bisa menyiasatinya:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

Tidak semua seds akan menafsirkan -input standar berarti, tetapi banyak yang menafsirkannya . ( POSIX mengatakan sed harus mendukung -mean-in standar jika pelaksana ingin -berarti standar-in ???)

Cara lain adalah menangani konten yang ditambahkan secara berurutan. Ada perintah lain yang menjadwalkan output dengan cara yang sama seperti read, dan sedakan menerapkannya serta read dalam urutan yang mereka buat. Ini sedikit lebih terlibat - itu memerlukan menggunakan satu seduntuk amengatur Pointerpertandingan ke output yang lain seddalam skripnya.

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file

Jadi pada dasarnya yang pertama sedmenulis sedskrip kedua , yang seddibaca kedua pada input standar (mungkin ...) dan berlaku pada gilirannya. Yang pertama sedhanya berfungsi pada kecocokan pertama untuk Pointerditemukan, dan setelah itu qmenggunakan input. Tugasnya adalah ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Pastikan bahwa semua pola karakter aman dengan garis miring terbalik karena yang kedua sedakan perlu menafsirkan setiap bit yang dibaca secara harfiah untuk memperbaikinya. Setelah selesai, letakkan salinan di Hruang lama.
  2. s|.*|/&/!p;//!d|p; x
    • Beri tahu yang kedua seduntuk pmematahkan setiap baris input !tetapi yang /&/baru saja kita selamatkan pola; dan kemudian dmenghapus semuanya. pletakkan perintah di yang kedua sed, kemudian xubah hbuffer pola lama dan bekerja pada salinan yang disimpan.
  3. s|.|r file1&a\\&|p;q
    • Satu-satunya karakter yang bekerja dengan kami di sini adalah \newline karena sedakan memiliki yang pertama ketika kita Hmenemukan garis sebelumnya. Jadi kita masukkan perintah r file1dan ikuti dengan \newline kita lalu perintah a\\untuk append diikuti juga oleh \newline. Semua sisa Hbaris kami mengikuti \newline terakhir itu.

Skrip yang ditulis pertama terlihat seperti ini:

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

Pada dasarnya yang kedua sedakan mencetak setiap baris tetapi yang pertama sedmengaturnya untuk adibelanjakan. Untuk baris tertentu, dua penundaan penulisan ke standard-out dijadwalkan - yang pertama adalah read of file1dan yang kedua adalah salinan dari garis yang kita inginkan setelahnya. sedDokter yang pertama bahkan tidak perlu dalam kasus ini (lihat? Tidak ada garis miring terbalik) tetapi penting untuk melarikan diri dengan aman seperti yang saya lakukan di sini setiap kali pencocokan pola digunakan kembali sebagai input.

Ngomong-ngomong, jadi ... ada beberapa cara.


2

Ini cukup sederhana dengan AWK:

File1 ke File2 sebelum pattern = "Pointer"

Pertama memuat konten File1 ke dalam variabel

f1="$(<File1)"

kemudian lakukan penyisipan

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(Atau, jika Anda ingin memasukkan File1 setelah "Pointer")

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2

2

cara pilihan saya: templating .

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

Ini akan mengganti setiap changeme kejadian di origfile dengan isi file2insert . Hapus g terakhir dari sed untuk mengganti hanya kemunculan pertama CHANGEME .


Bagaimana Anda bisa menggunakan $xdalam perintah pertama Anda, ketika itu hanya didefinisikan dalam perintah kedua Anda?
Totor

"$ x" pada perintah pertama hanya merupakan placeholder yang akan dievaluasi oleh envsubst pada perintah kedua. Dengan kutipan tunggal skrip sed, $ x tidak dievaluasi oleh shell Anda.
nrc

2

[Masukkan konten file ke file lain SEBELUM pola]

sed -i '/PATTERN/r file1' -e //N file2

[Setelah Pola]

sed -i '/PATTERN/r file1' file2

Nberfungsi dengan baik, tetapi tidak jika POLA cocok dengan input terakhir
Sundeep

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.