Bir dosyanın başına eklemek için bir astar kabuğu


Yanıtlar:


29

Aşağıdaki hack , işe yarayan ve çok sayıda olumlu oy alan hızlı bir yanıttı. Sonra, soru daha popüler hale geldikçe ve daha fazla zaman geçtikçe, öfkeli insanlar bunun işe yaradığını ama tuhaf şeyler olabileceğini ya da hiç işe yaramadığını bildirmeye başladı, bu yüzden bir süre öfkeyle reddedildi. Ne kadar eğlenceli.

Çözüm, sisteminizdeki dosya tanımlayıcılarının tam olarak uygulanmasından yararlanır ve uygulama, boşluklar arasında önemli ölçüde farklılık gösterdiğinden, başarısı tamamen sisteme bağlıdır, kesinlikle taşınabilir değildir ve belirsiz bir şekilde önemli olan hiçbir şey için güvenilmemelidir.

Şimdi, tüm bunların dışında cevap şuydu:


Dosya ( exec 3<> yourfile) için başka bir dosya tanımlayıcı yaratmak ve buna ( ) >&3yazmak, aynı dosya üzerinde okuma / yazma ikileminin üstesinden gelecektir . Benim için awk ile 600K dosyalarda çalışır. Ancak aynı numarayı 'kedi' kullanarak denemek başarısız olur.

Önpendajı bir değişken olarak awk ( -v TEXT="$text") ' ye geçirmek, bu hileyi' sed 'ile yapmayı engelleyen birebir alıntı probleminin üstesinden gelir.

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

3
UYARI: İlk satır dışında hiçbir şeyin değişmediğinden emin olmak için çıkışı kontrol edin. Bu çözümü kullandım ve işe yarıyor gibi görünse de, bazı yeni satırların eklenmiş gibi göründüğünü fark ettim. Bunların nereden geldiğini anlamıyorum, bu yüzden bu çözümün gerçekten bir sorunu olduğundan emin olamıyorum, ancak dikkatli olun.
conradlee

1
Yukarıdaki bash örneğinde, birden çok satırın başına eklenmiş olduğunu göstermek için çift tırnak işaretlerinin satır başı üzerine yayıldığını unutmayın. Kabuğunuzun ve metin düzenleyicinizin işbirliği içinde olup olmadığını kontrol edin. \ r \ n akla geliyor.
John Mee

4
Bunu dikkatli kullanın! Nedenini öğrenmek için aşağıdaki yoruma bakın: stackoverflow.com/questions/54365/…
Alex

3
Eğer şimdi güvenilmezse, cevabınızı daha iyi bir çözümle güncellemeye ne dersiniz?
zakdances

Bir hack , ancak ve ancak tam olarak neden çalıştığı ve dolayısıyla dikkatle gözlemlenen kısıtlamalar altında bilindiği takdirde yararlıdır . Sorumluluğun reddine rağmen, hack'iniz bu kriterleri karşılamıyor ("tamamen sisteme bağlı", "üstesinden geliyor", "benim için çalışıyor"). Feragatnamenizi ciddiye alırsak, hiç kimse hackinizi kullanmamalıdır - ve ben de kabul ediyorum. Peki elimizde ne kaldı? Gürültülü bir dikkat dağıtıcı. Ben iki seçenek göreceksiniz: (a) cevabınızı veya (b) açıklıyor uyarıcı bir masal çevirmek silme - bu yaklaşım - bu olabileceğinden cazip olamaz çalışmak genellikle .
mklement0

103

Bu hala bir geçici dosya kullanıyor, ancak en azından bir satırda:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

Kredi: BASH: Dosyanın Başına Metin / Satır Ekleme


4
-Sonra ne oluyor cat?
makbol

Sonrasına yeni bir satır eklemeden "metin" eklemenin bir yolu var mı?
chishaku

@chishakuecho -n "text"
Sparhawk

3
Bir uyarı: eğer yourfilebir sembolik bağlantı ise , bu istediğinizi yapmayacaktır.
dsh Shepherd

Cevap bundan farklı mı: echo "text"> / tmp / out; kedi dosyanız >> / tmp / out; mv / tmp / dosyanızın çıkışı ??
Richard


20

John Mee: Metodunuzun çalışacağı garanti edilmez ve 4096 bayttan fazla malzeme eklerseniz muhtemelen başarısız olacaktır (en azından gnu awk ile olan budur, ancak diğer uygulamaların da benzer kısıtlamaları olacağını düşünüyorum). Sadece bu durumda başarısız olmakla kalmayacak, aynı zamanda kendi çıktısını okuyacağı sonsuz bir döngüye girecek ve böylece mevcut alan dolana kadar dosyanın büyümesini sağlayacaktır.

Kendiniz deneyin:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(uyarı: bir süre sonra onu sonlandırın yoksa dosya sistemini doldurur)

Dahası, dosyaları bu şekilde düzenlemek çok tehlikelidir ve çok kötü bir tavsiye, sanki dosya düzenlenirken (çökme, disk dolu) dosyada tutarsız bir durumda kalmanız neredeyse garantidir.


6
Bu muhtemelen ayrı bir cevap değil, bir yorum olmalıdır.
lkraav

10
@Ikraav: belki, ancak "yorum" çok uzun ve ayrıntılı (ve kullanışlı), iyi görünmeyecek ve belki de StackOverflow'un sınırlı yorum kapasitesine uymuyor.
Hendy Irawan

18

Geçici dosya olmadan mümkün değil, ama işte oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

Geçici dosyalar olmadan yapmak için ed veya perl gibi diğer araçları kullanabilirsiniz.


16

Geçici dosyayı mktemp gibi bir yardımcı program kullanarak güvenli bir şekilde oluşturmanın , en azından komut dosyası kök ayrıcalıklarıyla çalıştırılacaksa, genellikle iyi bir fikir olduğunu belirtmekte fayda var . Örneğin şunları yapabilirsiniz (yine bash'da):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )

15

Kontrol ettiğiniz bilgisayarlarda buna ihtiyacınız varsa, "moreutils" paketini kurun ve "sünger" kullanın. O zaman şunları yapabilirsiniz:

cat header myfile | sponge myfile

@Vinko Vrsalovic'in cevabıyla birleştirildiğinde bu benim için iyi çalıştı:{ echo "prepended text"; cat myfile } | sponge myfile
Jesse Hallett

13

Heredoc bash kullanarak bir tmp dosyası ihtiyacını ortadan kaldırabilirsiniz:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

Bu işe yarar çünkü $ (cat myfile), bash betiği değerlendirildiğinde, yönlendirmeli cat çalıştırılmadan önce değerlendirilir.


9

Düzenlemek istediğiniz dosyanın my.txt olduğunu varsayarsak

$cat my.txt    
this is the regular file

Başına eklemek istediğiniz dosya başlıktır

$ cat header
this is the header

Başlık dosyasında son bir boş satır olduğundan emin olun.
Şimdi başına ekleyebilirsiniz

$cat header <(cat my.txt) > my.txt

İle sonuçlanırsın

$ cat my.txt
this is the header
this is the regular file

Bildiğim kadarıyla bu sadece 'bash'da çalışıyor.


bu neden çalışıyor? Parantezler ortadaki kedinin ayrı bir kabukta çalışmasına ve tüm dosyayı aynı anda dökmesine neden olur mu?
Catskul

<(Cat my.txt) 'nin dosya sisteminde bir yerde geçici bir dosya oluşturduğunu düşünüyorum. Bu dosya daha sonra orijinal dosya değiştirilmeden önce okunur.
cb0

Bir keresinde birisi bana "<()" sözdizimi ile neler yapabileceğinizi gösterdi. Ancak bu yöntemin nasıl adlandırıldığını hiç öğrenmedim, bash kılavuzunda da bir şey bulamadım. Birisi bunun hakkında daha fazla şey biliyorsa lütfen bana bildirin.
cb0

1
Benim için çalışmadı. Neden bilmiyorum. GNU bash, sürüm 3.2.57 (1) -release (x86_64-apple-darwin14) kullanıyorum, OS X Yosemite kullanıyorum. this is the headerMy.txt dosyasında iki satır ile bitirdim. Bash'i güncelledikten sonra bile 4.3.42(1)-releaseaynı sonucu alıyorum.
Kohányi Róbert

1
Bash geçici bir dosya oluşturmaz, sübstitüsyondaki ( <(...)) işlemdeki komutun yazdığı bir FIFO (boru) oluşturur, bu nedenle baştan tam olarak okunacağının garantisi yokturmy.txt , bu olmadan bu teknik çalışmaz.
mklement0

9

Kabuk komut dosyasında zorlaşan şeyleri yapmaya başladığınızda, betiği "uygun" bir kodlama dilinde (Python / Perl / Ruby / vb.)

Dosyaya bir satırın önceden eklenmesine gelince, bunu borulama yoluyla yapmak mümkün değildir, çünkü herhangi bir şey yaptığınızda cat blah.txt | grep something > blah.txt, yanlışlıkla dosyayı kapatır . Yükleyebileceğiniz adında küçük bir yardımcı program komutu vardır sponge(yaparsınız cat blah.txt | grep something | sponge blah.txtve dosyanın içeriğini arabelleğe alır, sonra dosyayı dosyaya yazar). Geçici bir dosyaya benzer, ancak bunu açıkça yapmanız gerekmez. ama bunun Perl'den "daha kötü" bir gereklilik olduğunu söyleyebilirim.

Bunu awk veya benzeri bir yöntemle yapmanın bir yolu olabilir, ancak kabuk-komut dosyası kullanmanız gerekiyorsa, geçici bir dosyanın açık arayla en kolay (/ yalnızca?) Yol olduğunu düşünüyorum.


7

Daniel Velkov'un önerdiği gibi, tee kullanın.
Bana göre, bu basit ve akıllı bir çözüm:

{ echo foo; cat bar; } | tee bar > /dev/null

7

DÜZENLEME: Bu bozuk. Kedi ve tişört içeren bir dosyanın başına eklerken tuhaf davranışları görün

Üzerine yazma sorununun geçici çözümü şudur tee:

cat header main | tee main > /dev/null

Bir süre asıldı. "Tee: main: Cihazda yer kalmadı" ile biter. main birkaç GB idi, ancak her iki orijinal metin dosyası sadece birkaç KB idi.
Marcos

3
Gönderdiğiniz çözüm bozuksa (ve aslında kullanıcıların sabit disklerini dolduruyorsa), topluluğa bir iyilik yapın ve cevabınızı silin.
aioobe

1
Yanlış cevapların silinmesi gerektiğini söyleyen bir kılavuza işaret edebilir misiniz?
Daniel Velkov

3

Kullandığım. Bu, istediğiniz şekilde sıralamayı, ekstra karakterleri vb. Belirlemenizi sağlar:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

Not: Yalnızca dosyalar ters eğik çizgi içeren metin içeriyorsa çalışmaz, çünkü bu, kaçış karakterleri olarak yorumlanır


3

Çoğunlukla eğlence / kabuk golf için, ancak

ex -c '0r myheader|x' myfile

hile yapacak ve ardışık düzenler veya yeniden yönlendirme yok. Tabii ki, vi / ex gerçekten etkileşimli olmayan kullanım için değildir, bu nedenle vi kısaca yanıp sönecektir.


Görevli bir komut kullandığı ve bunu basit bir şekilde yaptığı için benim açımdan en iyi tarif.
Diego

2

Neden sadece ed komutunu kullanmıyorsunuz (burada fluffle tarafından önerildiği gibi)?

ed tüm dosyayı belleğe okur ve otomatik olarak yerinde dosya düzenlemesi yapar!

Yani, dosyanız o kadar büyük değilse ...

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

Yine başka bir geçici çözüm, Jürgen Hötzel tarafından önerildiği gibi sed 's / c / d /' myFile'dan myFile'a Redirect çıktısında açık dosya tanıtıcılarını kullanmak olabilir.

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

Elbette tüm bunlar tek bir satıra yazılabilir.


2

Sabit metnin başına eklemek için cb0'ın "geçici dosya yok" çözümünün bir varyantı:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

Yine bu, kedinin girdi ve çıktı için aynı dosyaya sahip olmayı reddetmesini önlemek için alt kabuk uygulamasına - (..) - dayanır.

Not: Bu çözümü beğendim. Ancak, Mac'imde orijinal dosya kayboldu (olmaması gerektiğini düşündü ama kayboluyor). Bu, çözümünüzü şu şekilde yazarak düzeltilebilir: echo "başa eklenecek metin" | cat - file_to_be_modified | cat> tmp_file; mv tmp_file file_to_be_modified


1
Ben de orijinal dosyanın kaybolduğunu buldum. Ubuntu Lucid.
Hedgehog

2

İşte keşfettiğim şey:

echo -e "header \n$(cat file)" >file

1
Bu, @nemisj tarafından yapılan önceki öneriyle aynı. Hayır?
Hedgehog


2

UYARI: OP'nin ihtiyaçlarını karşılamak için biraz daha fazla çalışmaya ihtiyaç vardır.

Kaygılarına rağmen @shixilun tarafından sed yaklaşımının işe yaramasını sağlamanın bir yolu olmalı. Bir dosyayı sed ikame dizesine okurken boşluktan kaçmak için bir bash komutu olmalıdır (örneğin, satırsonu karakterlerini '\ n' ile değiştirin. Kabuk komutları visve catyazdırılamayan karakterlerle ilgilenebilir, ancak beyaz boşluklarla ilgilenemez, bu nedenle bu OP'leri çözmez sorun:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

Bu SO cevabı gibi kabuğu ve sed'i mutlu tutmak için, yerine bir satır devam karakteri () ve belki de ardından bir & ile eklenmesi gereken, yedek komut dosyasındaki ham satırsonları nedeniyle başarısız olur

sed global olmayan arama-değiştirme komutları için boyut sınırı 40K'dır (örüntüden sonra / g yok), bu nedenle anonim olan awk'nin korkutucu arabellek taşması sorunlarından kaçınılır.


2
sed -i -e "1s/^/new first line\n/" old_file.txt

tam olarak aradığım şey, ama aslında sorunun doğru cevabı değil
masterxilo

2

İle $ (komut) bir değişkene bir komutun çıktısını yazabilir. Bu yüzden bunu bir satırda üç komutla yaptım ve geçici dosya yok.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile

1

Büyük bir dosyanız varsa (benim durumumda birkaç yüz kilobayt) ve python'a erişiminiz varsa, bu catboru çözümlerinden çok daha hızlıdır :

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'


1

Aşağıdakilerle bir çözüm printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

Şunları da yapabilirsiniz:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

Ancak bu durumda %, yorumlanabileceği ve sonuçlarınızı altüst edebileceği için, hedef dosyanın içeriği dahil hiçbir yerde olmadığından emin olmalısınız .


2
Hedef dosyanızda printf'in bir biçimlendirme seçeneği olarak yorumladığı herhangi bir şey varsa, bu patlayacak (ve dosyanızı kesecek!) Gibi görünüyor.
Alan H.

echodaha güvenli bir seçenek gibi görünüyor. echo "my new line\n$(cat my/file.txt)" > my/file.txt
Alan H.

@AlanH. Cevaptaki tehlikeler konusunda uyarıyorum.
user137369

Aslında, ${new_line}hedef dosyadaki yüzdeler hakkında uyardınız
Alan H.

Daha açık konuşabilir misin? Bu gerçekten büyük bir uyarı IMO. "Herhangi bir yer" bunu çok açık hale getirmiyor.
Alan H.

1

Perl komut satırını kullanabilirsiniz:

perl -i -0777 -pe 's/^/my_header/' tmp

-İ dosyanın bir satır içi değişimini oluşturacak ve -0777 tüm dosyayı bulandıracak ve ^ ile yalnızca başlangıcı eşleştirecektir. -pe tüm satırları yazdırır

Veya my_header bir dosyaysa:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

/ E'nin değiştirmede bir kod değerlendirmesine izin vereceği yer.


0
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

burada "my_file", "my_string" in başına eklenecek dosyadır.


0

Ben sevmeye başladım fluffle en @ ed yaklaşımı iyi. Sonuçta, herhangi bir aracın komut satırı anahtarları ile komut dosyası içeren düzenleyici komutları esasen burada aynı şeydir; komut dosyalı bir düzenleyici çözümünün "temizliğinin" daha az olduğunu veya olmadığını görmemek.

Mesajları işlemek için bir .git/hooks/prepare-commit-msgdepo içi .gitmessagedosyanın başına eklenen tek satırlık yazım :

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

Örnek .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

Bunun 1ryerine yapıyorum 0r, çünkü bu, orijinal şablondan dosyanın üstüne boş yazmaya hazır satırı bırakacak. .gitmessageO zaman üstüne boş bir satır koymayın , sonunda iki boş satırla karşılaşacaksınız. -sed'in teşhis bilgisi çıktısını bastırır.

Bunu yapmakla bağlantılı olarak, vim-buff'lar için şunlara sahip olmanın da iyi olduğunu keşfettim :

[core]
        editor = vim -c ':normal gg'

0

değişkenler, ftw?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list

0

Bence bu, ed'in en temiz çeşidi:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

işlev olarak:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile

0

BASH'de komut dosyası yazıyorsanız, aslında, yalnızca şunları yayınlayabilirsiniz:

kedi - dosyanız / tmp / dışarı && mv / tmp / dosyanızın dışına

Aslında bu, kendi sorunuza gönderdiğiniz Karmaşık Örnek'de.


Bir editör bunu olarak değiştirmeyi önerdi cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile, ancak bu benim cevabımdan yeterince farklı, kendi cevabı olması gerekiyor.
Tim Kennedy

0

IMHO tutarlı ve güvenilir olursa olsun iki dosyanın boyutları çalışacağına dair hiçbir kabuk çözümüdür (ve asla biri olacak) myheaderve myfile. Bunun nedeni, bunu geçici bir dosyada yinelemeden (ve kabuğun sessizce geçici bir dosyaya tekrarlamasına izin vermeden, örneğin exec 3<>myfile, borulama teevb. Yapılar aracılığıyla yapmak istemenizdir .

Aradığınız "gerçek" çözüm, dosya sistemi ile uğraşmak zorundadır ve bu nedenle kullanıcı alanında mevcut değildir ve platforma bağlı olacaktır: kullanımdaki dosya sistemi işaretçisini dosya sistemi işaretçisinin myfilemevcut değerine göre değiştirmenizi istiyorsunuz için myheaderve dosya sisteminde yerini EOFait myheadertarafından işaret şimdiki dosya sistemi adresine bir zincirleme bağlantı ile myfile. Bu önemsiz değildir ve açıkçası süper kullanıcı olmayanlar tarafından yapılamaz ve muhtemelen süper kullanıcı tarafından da yapılamaz ... İnode ile oynayın, vb.

Yine de, döngü cihazlarını kullanarak bunu az çok taklit edebilirsiniz. Örneğin bu SO başlığına bakın .

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.