Bir dosyanın içeriğini bir desenden (işaretleyici) önce başka bir dosyaya nasıl ekleyebilirim?


32

File1 içeriği:

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

File2 içeriği:

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

Perl / shell betiğinin yürütülmesinden sonra, File2içerik şöyle olmalı:

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"

yani içeriğini yapıştırın File1içinde File2"Pointer" içerir satırdan önce.




Zaten bu soruyu kapatmak için SO sorusunu oyladım, bu soruyu kapatmak için daha fazla neden yok. BTW, soru SO üzerinde çok daha kötü bir kalite, bu yüzden mantık bunu kapatmak değil, bunu dikte ediyor.
peterh, Monica

Yanıtlar:


33

sed Bunun için bir işlevi vardır ve satır içi değişikliği yapabilirsiniz:

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

Ancak bu, İşaretçi satırınızı file1'in üzerine getirir. Aşağıya koymak için, satır çıktısını geciktirin:

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

8
Lütfen ne -e 1x -e '2,${x;p}' -e '${x;p}'yapabileceğini açıklar mısın? Desen arabelleğinde bir şeyler değiş tokuş yaptığınızı ve sonra onu yazdırdığınızı anlıyorum -n;
hdl

@ jfg956 'Pointer' bölümünü sadece orijinal dosyadan değiştirmek ve silmek mümkün müdür. Bunu ikinci bir sedye süpürgesiyle çözebilirim, ancak bir seferde yapmak mümkün mü?
Alexander Cska

17

Kullanmadan sedveya awk...

İlk önce, modeliniz olan çizginizi bulun:

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

Ardından, istenen sonucu elde etmek için 3 komut kullanın:

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

Bu 3 kez dosya erişmenin dezavantajı var file2ama daha net olabilir sedait awkçözümü.


10

awkBunu oldukça kolaylaştırır.
Dosyadan önce satırı ekleyin:

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

İç dosyayı Pointersatırdan sonra yazdırmak için desenlerin sırasını değiştirmeniz (varsayılan eylemi elde etmek için noktalı virgül eklemeniz gerekir) ve linedeğişkeni bırakabilirsiniz :

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

Ve henüz kimsenin kullanmadığı için 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

onun çalışma, ancak işaretçi içeren satırı kaldırıldı
user1228191

Benzer şekilde, 2 dosya içindeki 1 numaralı dosyanın içeriklerini awk
user1228191

@ user1228191 İlk düzeltildi, ikinci eklendi.
Kevin,

'Perl' sürümü çalışmıyor gibi görünüyor. konsola system("cat innerfile")çıktılar innerfile. Bir şey mi eksik?
kaartic

awk komutu [gawk '/ <body> / {while (getline satırı <"$ HOME / bin / SrunScenario.style") {print line}} // // index.html> new_index.html] sadece milyonlarca satır atlar ve yazdırır . gawk V4.2.0 Burada neyi özlüyorum?
JESii

7

Şunun için kolay bir iş ed:

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

-r file1belirtilen dosyada adreslenmiş satırdan sonra okur, bu durumda ilk satır eşleşmesinden önceki satırdır Pointer. Böylece , birden fazla satırda meydana file2gelse bile , bu yalnızca bir kez içeriğe eklenecektir Pointer. Her eşleşen satırdan önce eklemek istiyorsanız, global bayrağı ekleyin :

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

Değiştir ,pile wsize yerinde dosyasını düzenlemek istiyorsanız.


Kabul edilen sedcevap çoğu durumda işe yarar ancak işaretleyici son satırdaysa, komut beklendiği gibi çalışmaz: File1işaretleyiciden sonra içeriğini ekler .
Başlangıçta denedim:

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

aynı zamanda iyi çalışıyor ( rdöngünün sonunda büyüsünü yapacak) ancak işaretçi son satırdaysa da aynı problemi Nyaşıyor (son satırdan sonra ext satırı yok ). Bu soruna geçici bir çözüm bulmak için, girişinize yeni bir satır ekleyebilirsiniz:

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)

Bu, file2eşleşen her satırdan önce içerik ekleyecektir . Sadece ilk eşleşen satırdan önce eklemek için bir loop kullanabilir ve ndosya sonuna ulaşana kadar sadece ext satırını çekebilirsiniz :

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

Bu sedçözümlerle yerinde düzenleme özelliğini kaybedersiniz (ancak başka bir dosyaya yönlendirebilirsiniz).


6

Dosya2'deki satırları okumak için bir döngü kullanın. Pointerİle başlayan bir çizgi bulursanız , o zaman dosya1'i yazdırın. Bu aşağıda gösterilmiştir:

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

4

Bu konuda devam etmek için birkaç yol var sed. Bunun bir yolu, kabul edilen cevapta önerilen şekilde gecikmeli bir okumadır. Ayrıca şöyle yazılabilir:

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

... başka bir yerde tutma tamponu ile uygulanan geriye dönük görünüm yerine biraz belirgin bir görünüm ile. Bu nedeniyle kaçınılmaz olarak @don_crissti notları, ancak, bu son hat ile aynı sorun olacaktır N yapar hattı döngüsü arttırma ve rEAD komut hattı sayısı ile tatbik edilir.

Çevresinde alabilirsiniz:

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

Tüm sedler yorumlayacaktır -standart girdi, ancak birçok do anlamında. ( POSIX , uygulayıcı standart-giriş demek istiyorsa standart giriş yapmayı seddesteklemeli diyor ?)--

Başka bir yol da eklenen içeriği sırayla ele almaktır. Orada başka bir komuttur programları aynı şekilde çıktı rEAD yapar ve sedonu ve uygulayacaktır ronlar senaryosunu sırayla inceliyoruz EAD. O birini kullanarak gerektirir - Yine biraz daha yer var sedetmek append Pointerdiğerinin çıkışına maçı sedkendi komut.

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

Bu nedenle, temel olarak birincisi sed, ikinciye standart girdiyi (belki ...) okuduğu ve sırayla uyguladığı sedbir komut dosyasını yazar . İlki, yalnızca bulunan ilk eşleşmede çalışır ve daha sonra girdiyi bırakır. Görevi ...sedsedPointerq

  1. s/[]^$&\./*[]/\\&/g;H
    • Tüm desen karakterlerinin güvenli bir şekilde ters eğik çizgiden kaçtığından emin olun, çünkü ikincisi sedkelimenin tam anlamıyla doğru yapmak için okuduğu her biti yorumlamalıdır. Bu işlem bittikten sonra Heski alana kopyalayın .
  2. s|.*|/&/!p;//!d|p; x
    • İkinci söyle sediçin pher giriş hattını Rint !ama /&/biz sadece desen safed birini; ve sonra daynısını seçmek için. pkomutları ikinci adımda uygulayın sed, sonra eski ve kalıp tamponlarını kaydedilmiş kopyamız üzerinde çalışacak şekilde xdeğiştirin h.
  3. s|.|r file1&a\\&|p;q
    • Burada birlikte çalıştığımız tek karakter bir \nip çizgisi çünkü seddaha Hönce çizgiyi belirlediğimizde bir tane hazırlamış olacak . Biz komutu eklemek Yani r file1bizim ile onu takip \newline sonra komut a\\için append bir tarafından da takip \newline. Alan çizgimizin geri kalanının tamamı Hbu son satırda izler \n.

İlk yazdığı senaryo şuna benzer:

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

Temel olarak ikincisi sedher satırı yazdırır, ancak ilk satır ppend sediçin aayarlar. Söz konusu hat için iki standart aşımı bulundukları Gecikmeli yazma planlanan - ilk rve ead file1ve ikinci biz ondan sonra istediğiniz satırın bir kopyasıdır. İlk seddoktorun doktoru bu durumda bile gerekli değildir (bakınız? Ters eğik çizgi yok) ancak bir desen eşleşmesi girdi olarak tekrarlandığında burada yaptığım şekilde güvenli bir şekilde kaçmak önemlidir.

Neyse, yani ... birkaç yol var.


2

Bu AWK ile oldukça basittir:

Pattern1'den önce File2 içine File1 = "Pointer"

İlk önce File1 içeriğini bir değişkene yükleyin

f1="$(<File1)"

sonra ekleme yap

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

(Veya, "İşaretçi" den sonra Dosya1'i eklemek istiyorsanız)

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

2

tercih ettiğim yol: baştan çıkarıcı .

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

Bu, orijinal dosyadaki her CHANGEME oluşumunu file2insert içeriğiyle değiştirir . Geçen çıkarın g yalnızca ilk geçtiği yerde değiştirmek için sed den changeme .


$xİlk komutunuzda, sadece ikinci komutunuzda tanımlandığında nasıl kullanabilirsiniz ?
Totor,

ilk komuttaki "$ x" sadece ikinci komutta envsubst tarafından değerlendirilecek bir yer tutucudur. Tekli sed script tırnak işaretleriyle $ x, kabuğunuz tarafından değerlendirilmez.
nrc

2

[Desenden ÖNCE dosya içeriğini başka bir dosyaya ekleyin]

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

[Desenden Sonra]

sed -i '/PATTERN/r file1' file2

Ngüzel çalışır, ancak PATTERN girişin son satırına
uymuyorsa

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.