Bir bash borusunda ham ikili verileri nasıl işleyebilirim?


15

Bir parametre olarak bir dosya alır bir bash işlevi var, dosyanın var olduğunu doğrular, sonra stdin çıkan dosyaya bir şey yazar. Saf çözüm metin için iyi çalışıyor, ancak keyfi ikili verilerle ilgili sorunlar yaşıyorum.

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done

Yanıtlar:


15

Yolunuz, ayırıcı ( $IFS) öğesinin okumayı bölmek için kullandığı alanın içine yazdığı her şeye satır sonları eklemektir . Yeni satırlara ayırmak yerine, her şeyi alın ve aktarın. Yukarıdaki kodun tamamını buna indirgeyebilirsiniz:

 cat - > $file

Kesik bitine ihtiyacınız yoktur, bu kesilir ve tüm STDIN akışını buna yazar.

Düzenleme: Eğer zsh kullanıyorsanız sadece > $filekedi yerine kullanabilirsiniz . Bir dosyaya yönlendiriyorsunuz ve kesiyorsunuz, ancak orada asılı duran bir şey varsa STDIN'i kabul etmek için o noktada okunacaktır. Bence bash ile böyle bir şey yapabilirsiniz ama özel bir mod ayarlamanız gerekir.


Stdin yönlendirme örneğini çalıştıramadım, ancak kedi örneğini> | (Ben noclobber set var) bir cazibe gibi çalışır. Günümü yaptığınız için teşekkürler ^. ^
David Souther

Kedi olmayan sürüm için +1. Her zaman işe yaramaz kedilerden kaçının;)
rozcietrzewiacz

@rozcietrzewiacz: Doğru, ancak sonradan düşünülmüş bir şeydi ve yanılmışım. Bu kedinin faydasız kullanımı olmayabilir. Yapabileceğiniz tek şey > $file. Bu yalnızca üst kabuk betiğinde stdin'i arayan ilk şey olarak çalışır. Temelde David'in tüm kodları tek bir karaktere indirgenebilir, ancak bence cat -daha zarif ve daha az sorun ortaya çıkıyor çünkü görüşte anlaşılıyor.
Caleb

Bazen catUUOC fanatiklerini kızdırmak için dört ya da beş s birlikte bağlarım
Michael

@MichaelMrozek: Bazen veri dosyalarımı adlandırıyorum, catböylece kullanmakta ısrar eden insanlar kodu okumak için zihinsel jimnastik yapmak zorundalar. Adlandırılmış borular da iyi hedeflerdir.
Caleb

7

Bir metin dosyasını tam anlamıyla okumak için read, çıktıyı iki şekilde işleyen düz kullanmayın :

  • read\kaçış karakteri olarak yorumlar ; read -rbunu kapatmak için kullanın .
  • readkarakterleri kelimelere böler $IFS; IFSbunu kapatmak için boş bir dizeye ayarlayın .

Bir metin dosyasını satır satır işlemek için kullanılan genel deyim

while IFS= read -r line; do 

Bu deyimin açıklaması için bkz. Neden while IFS= readyerine bu kadar sık ​​kullanılıyor IFS=; while read..? .

Bir dizeyi tam olarak yazmak için echo, dizeyi iki şekilde işleyen yalnızca düz kullanmayın :

  • Bazı mermilerde echosüreçler ters eğik çizgiden kaçar. (Bash'da, xpg_echoseçeneğin ayarlanıp ayarlanmadığına bağlıdır .)
  • Birkaç dizge seçenek olarak kabul edilir, örneğin -nveya -e(kesin küme kabuğa bağlıdır).

Bir dizgeyi tam anlamıyla yazdırmanın taşınabilir bir yolu vardır printf. (Girişinizin bir seçenek gibi görünmediğini bilmiyorsanız bash'da daha iyi bir yol yoktur echo.) Tam dizeyi yazdırmak için ilk formu ve yeni satır eklemek istiyorsanız ikinci formu kullanın.

printf %s "$line"
printf '%s\n' "$line"

Bu yalnızca metin işlemek için uygundur , çünkü:

  • Kabukların çoğu girişteki boş karakterleri boğar.
  • Son satırı okuduğunuzda, sonunda bir satırsonu olup olmadığını bilmenin bir yolu yoktur. (Giriş yeni satırla bitmezse bazı eski mermiler daha büyük sorun yaşayabilir.)

Kabuktaki ikili verileri işleyemezsiniz, ancak çoğu unice'deki yardımcı programların modern sürümleri rastgele verilerle başa çıkabilir. Tüm girişleri çıkışa iletmek için tuşunu kullanın cat. Teğet olmak, echo -n ''hiçbir şey yapmanın karmaşık ve taşınabilir olmayan bir yoludur; echo -naynı derecede iyi (veya kabuğa bağlı olmayan) ve :daha basit ve tamamen taşınabilir.

: >| "$file"
cat >>"$file"

veya daha basit,

cat >|"$file"

Bir komut dosyasında, genellikle kullandığınız gerekmez >|çünkü noclobbervarsayılan olarak kapalıdır.


xpg_echo işaret ettiğiniz için teşekkürler, bu aslında benim kod başka bir yerde yaşıyordu ve hatta fark etmedi bir sorun. Noclobber, bashrc'ımda açma alışkanlığım var.
David Souther

0

Bu tam olarak ne istediğinizi yapacak:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

Yine de bellek kullanımını not edin. Bu, girdiyi boş sınırlandırılmış şekilde okur.

Girişte \0 boş bayt yoksa, bash'ın önce girişin tüm içeriğini belleğe okuması ve sonra çıkışı yapması gerekir.

Kesik adımınızla ilgili:

echo -n '' >| "$file" #Truncate the file

çok daha basit ve eşdeğer:

> ${file}   #Truncate the file
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.