Dosya okuma ve yazma: tee command


10

Böyle bir komutun iyi olduğu bilinmektedir:

cat filename | some_sed_command >filename

komuttan önce yürütülen çıkış yeniden yönlendirmesi, dosya adının kısaltılmasına neden olduğundan dosya dosya adını siler.

Sorunu şu şekilde çözebiliriz:

cat file | some_sed_command | tee file >/dev/null

ancak bunun her durumda işe yarayacağından emin değilim: dosya (ve sed komutunun sonucu) çok büyükse ne olur? İşletim sistemi hala okunmayan içeriğin üzerine yazmaktan nasıl kaçınabilir? Ben de her durumda çalışması gereken bir sünger komutu olduğunu görüyorum: tee "daha güvenli" mi?


Ana hedefiniz nedir? (basit terimlerle)
Sergiy Kolodyazhnyy

@Serg sadece şeylerin nasıl çalıştığını anlıyor ... Kos tarafından yazılan cevap konuyu açıklığa kavuşturuyor
VeryHardCoder

Yanıtlar:


10

Sorunu şu şekilde çözebiliriz:

cat file | some_sed_command | tee file >/dev/null

Hayır .

Şansı filedüşürme şansı olacak, ancak cat file | some_sed_command | tee file >/dev/nullkısalmayacağının garantisi yok file.

Her şey, ilk önce hangi komutun işleneceğine bağlıdır, birinin ne beklediğinin aksine , bir borudaki komutlar soldan sağa işlenmez . İlk olarak hangi komutun seçileceğine dair bir garanti yoktur, bu yüzden biri rastgele seçildiğini düşünebilir ve rahatsız edici olanı seçmeyen kabuğa asla güvenemez.

Üç komut arasında ilk olarak rahatsız edici komutun seçilme şansı, iki komut arasında ilk olarak rahatsız edici komutun seçilme şansından daha düşük olduğundan, kesilme olasılığı düşüktür file, ancak yine de gerçekleşecektir .

script.sh:

#!/bin/bash
for ((i=0; i<100; i++)); do
    cat >file <<-EOF
    foo
    bar
    EOF
    cat file |
        sed 's/bar/baz/' |
        tee file >/dev/null
    [ -s file ] &&
        echo 'Not truncated' ||
        echo 'Truncated'
done |
    sort |
    uniq -c
rm file
% bash script.sh
 93 Not truncated
  7 Truncated
% bash script.sh
 98 Not truncated
  2 Truncated
% bash script.sh
100 Not truncated

Bu yüzden asla böyle bir şey kullanmayın cat file | some_sed_command | tee file >/dev/null. spongeOli'nin önerdiği gibi kullanın .

Alternatif olarak, daha yoğun ortamlar ve / veya nispeten küçük dosyalar için burada herhangi bir komut çalıştırılmadan önce dosyayı okumak için burada bir dize ve bir komut değişikliği kullanılabilir:

$ cat file
foo
bar
$ for ((i=0; i<100; i++)); do <<<"$(<file)" sed 's/bar/baz/' >file; done
$ cat file
foo
baz

9

İçin sedözel olarak, onun kullanabilirsiniz -iin-yer argüman. Sadece açtığı dosyaya geri kaydeder, örneğin:

sed -i 's/ /-/g' filename

Daha iyi bir şey yapmak istiyorsanız sed, evet, bundan daha fazlasını yaptığınızı varsayarak , her şeyi dosyaya yazmadan önce tüm stdinleri "emecek" sponge( moreutilspaketten) ile arabelleğe alabilirsiniz . Gibi teeama daha az işlevsellik ile. Temel kullanım için, hemen hemen bir yedek değiştirme:

cat file | some_sed_command | sponge file >/dev/null

Bu daha güvenli mi? Kesinlikle. Muhtemelen sınırları vardır, bu yüzden devasa bir şey yapıyorsanız (ve sed ile yerinde düzenleme yapamıyorsanız), ikinci bir dosyada ve ardından mvbu dosyayı orijinal dosya adına geri düzenlemelerinizi yapmak isteyebilirsiniz . Bu atomik olmalıdır (bu nedenle, sürekli erişime ihtiyaç duymaları durumunda bu dosyalara bağlı herhangi bir şey kırılmaz).


0

Vim'i Ex modunda kullanabilirsiniz:

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

  2. ! Komutu çalıştır

  3. x Kaydet ve çık


0

Oh, ama spongetek seçenek değil; moreutilsbunun düzgün çalışması için elde etmek zorunda değilsiniz . Herhangi bir mekanizma, aşağıdaki iki gereksinimi karşıladığı sürece çalışır:

  1. Çıktı dosyasının adını parametre olarak kabul eder.
  2. Çıktı dosyasını yalnızca tüm girdiler işlendikten sonra oluşturur.

OP'nin bahsettiği iyi bilinen sorun, kabuğun boru hattındaki komutları yürütmeye başlamadan önce boruların çalışması için gerekli olan tüm dosyaları oluşturmasıdır, bu yüzden gerçekten kesilen kabuktur komutlardan herhangi birinin yürütülmeye başlama şansı bile olmadan çıkış dosyası (maalesef giriş dosyasıdır).

teeKomut, iş olmasa bile o tatmin ilk şartı da, ikinci gereksinimi karşılamak değil çünkü: düz çıkış dosyasına bir boru oluşturma gibi bad esasen bu yüzden her zaman, başlangıç hemen sonra çıkış dosyası oluşturur. (Aslında daha da kötüsü, çünkü kullanımı çıktı dosyası kesilmeden önce deterministik olmayan rastgele bir gecikme getirir, bu yüzden aslında işe yaramazken çalıştığını düşünebilirsiniz.)

Bu nedenle, bu sorunu çözmek için ihtiyacımız olan tek şey, herhangi bir çıktı üretmeden önce tüm girdilerini arabelleğe alacak ve çıktı dosya adını parametre olarak kabul edebilecek bir komuttur, böylece çıktısını çıktı dosyası. Böyle bir komut shuf. Yani, aşağıdakiler aynı şeyi başaracaktır sponge:

    shuf --output=file --random-source=/dev/zero 

--random-source=/dev/zeroBölüm hileler shufhiç bir üreticimizin yapmadan olan şey yapıyor içine, bu yüzden onu değiştirmeden girişi tamponlayacaktır.

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.