Linux: Bir dosyayı aynı anda hem girdi hem de çıktı olarak nasıl kullanabilirim?


55

Sadece bash olarak aşağıdakileri çalıştırın:

uniq .bash_history > .bash_history

ve tarihçem tamamen boş kaldı.

Sanırım yazmadan önce tüm dosyayı okuyacak bir yola ihtiyacım var. Bu nasıl yapılır?

Not: Belli ki geçici bir dosya kullanmayı düşündüm, ancak daha şık bir çözüm arıyorum.


Çünkü dosyalar sağdan sola açılıyor. Ayrıca bakınız stackoverflow.com/questions/146435/…
WheresAlice

Çıktıyı aynı dizindeki yeni bir dosyaya yazmalı ve eski dosyanın üstüne yeniden adlandırmalısınız. Diğer herhangi bir yaklaşım, yarıda kesilmesi durumunda verilerinizi kaybetme riskini doğuracaktır. Bazı araçlar bu adımı sizden gizleyebilir.
kasperd

Veya bashHISTCONTROL'ü yok sayılacakları içerecek şekilde ayarlarsanız geçmişine ardışık kopyalar koymaz; man sayfasına bakınız.
dave_thompson_085

Yanıtlar:


49

Ben kullanmanızı tavsiye spongegelen moreutils . Manpage'den:

DESCRIPTION
  sponge  reads  standard  input  and writes it out to the specified file. Unlike
  a shell redirect, sponge soaks up all its input before opening the output file.
  This allows for constructing pipelines that read from and write to the same 
  file.

Bunu probleminize uygulamak için şunu deneyin:

uniq .bash_history | sponge .bash_history

6
Kedi gibi, ama emme yetenekleri var: D
MilliaLover

77

Sadece basit ve sünger kullanmayan başka bir cevaba katkıda bulunmak istedim (çünkü genellikle hafif ortamlara dahil değildir).

echo "$(uniq .bash_history)" > .bash_history

İstenilen sonuca sahip olmalıdır. Alt kabuk .bash_history yazılmadan önce açılır. Phil P'in cevabında açıklandığı gibi, .bash_history orijinal komutta okunursa, zaten '>' operatörü tarafından kesildi.


15
Normalde, zaten geçerli, kabul edilmiş bir cevabı olan eski bir soruyu soran cevap hayranı değilim - ama bu zarif, iyi yazılmış ve gerekliliği (hafif ortamlar) için güçlü bir tartışma yapan; benim için varolan cevaplara gerçekten bir şeyler ekliyor. SF, Hart'a hoş geldiniz (bir aydır buradasınız, ancak bence bu sizin ilk yazınız). Bunun gibi sizden daha fazla cevap okumayı umuyorum!
MadHatter

4
Bu en iyi çözümdür. $()Bazı kaçan sorunlar nedeniyle backticks yerine bir subshell kullanmak zorunda kaldım .
CMCDragonkai

3
Bu çözümün büyük dosyalara ölçeklenip ölçeklenmediğini merak ediyorum, diyelim ki ... 20 veya 50 GB.
Amit Naidu

1
Bu gerçekten kabul cevap olmalı.
maxywb

1
Bu cevabı echo "$(fmt -p '# ' -w 50 readme.txt)" > readme.txtbugün yapmak için kullandım . Zarif bir çözüm için uzun süredir etrafta arama yapıyorlardı. Çok teşekkürler, @Hart Simha!
shredalert

12

Sorun şu ki, kabuğunuz komutları çalıştırmadan önce komut satırını ayarlıyor. Bu "giriş ve çıkış" meselesi değil, dosya içeriği uniq çalıştırılmadan önce zaten gider. Gibi bir şey gider:

  1. Kabuk, >çıktı dosyasını yazmak için açar , keser
  2. Kabuk, dosya çıktısının 1 (stdout için) bu çıktı için kullanılmasını sağlar.
  3. Kabuk uniq'i çalıştırır, belki execlp ("uniq", "uniq", ".bash_history", NULL gibi bir şey)
  4. uniq çalışır, açılır .bash_history ve orada hiçbir şey bulamaz

Yerinde düzenleme ve diğerlerinin bahsettiği geçici dosya kullanımı dahil çeşitli çözümler vardır, ancak asıl sorun sorunu, gerçekte neyin yanlış gittiğini ve nedenini anlamaktır.


9

Bunu yapmadan başka bir hile yapmak sponge, aşağıdaki komuttur:

{ rm .bash_history && uniq > .bash_history; } < .bash_history

Bu, backreference.org'daki dosyaların yerinde düzenlendiği mükemmel makalede açıklanan hilelerden biridir.

Temelde okumak için dosyayı açar, sonra onu "kaldırır". Yine de gerçekten kaldırılmadı: Ona işaret eden açık bir dosya tanıtıcısı var ve açık kaldığı sürece, dosya hala buralarda. Daha sonra aynı isimde yeni bir dosya oluşturur ve benzersiz satırları ona yazar.

Bu çözümün dezavantajı: Herhangi uniqbir nedenle başarısız olursa, geçmişiniz ortadan kalkacak.



3

Bu sedkomut dosyası bitişik kopyaları kaldırır. -iSeçeneği ile yerinde değişiklik yapar. Bu geliyor sed infodosyası:

sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history

sed hala temp dosyasını kullanıyor, straceresimli bir cevap ekledi (gerçekten önemli değil) :-)
Kyle Brandt

3
@Kyle: Doğru, ama "görüş alanı dışında, akıldan çıkmadı". Şahsen, açık geçici dosyayı kullanırdım çünkü geçici bir dosyadan kaçınmak için hile process input > tmp && mv tmp inputkullanmaktan çok daha basit ve daha okunaklıdır sedve başarısız olursa orijinal belgemin üzerine yazmaz ( sed -iincelikli bir şekilde başarısız olup olmadığını bilmiyorum - yapardım) olacağını düşünüyorum). Ayrıca, bu sedsenaryodan çok daha fazla ilgili bir şey olmadan yerinde gerçekleştirilemeyen dosya çıktısı yöntemiyle yapabileceğiniz birçok şey var . Bunları bildiğini biliyorum ama bazı izleyicilere faydası olabilir.
Dennis Williamson,

3

İlginç bir haberleşme olarak, sed de geçici bir dosya kullanır (bu sadece sizin için yapar):

$ strace sed -i 's/foo/bar/g' foo    
open("foo", O_RDONLY|O_LARGEFILE)       = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096)               = 4
write(4, "bar\n"..., 4)                 = 4
read(3, ""..., 4096)                    = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedPmPv9z", "foo")            = 0
close(1)                                = 0
close(2)                                = 0

Açıklama:
Tempfile ./sedPmPv9zfd 4, foodosyalar fd 3 olur. Okuma işlemleri fd 3'te, fd 4'te (temp dosyası) yazar. Foo dosyası daha sonra yeniden adlandırma çağrısında temp dosyasıyla değiştirilir.



0

Söz konusu komut yerinde düzenlemeyi desteklemediği sürece, geçici bir dosya oldukça fazladır ( uniqnot - some seds do ( sed -i)).


0

Vim'i Ex modunda kullanabilirsiniz:

ex -sc '%!uniq' -cx .bash_history
  1. % tüm satırları seç

  2. ! run komutu

  3. x kaydet ve kapat


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.