Bagaimana saya bisa menangani data biner mentah dalam pipa bash?

15

Saya memiliki fungsi bash yang mengambil file sebagai parameter, memverifikasi file yang ada, kemudian menulis apa pun yang datang stdin ke file. Solusi naif berfungsi dengan baik untuk teks, tetapi saya mengalami masalah dengan data biner sewenang-wenang.

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
David Souther
sumber

Jawaban:

15

Cara Anda adalah menambahkan jeda baris untuk setiap hal yang ditulisnya di ruang pemisah apa pun ( $IFS) yang digunakan untuk membagi baca. Alih-alih memecahnya menjadi baris baru hanya mengambil semuanya dan meneruskannya. Anda dapat mengurangi seluruh bit kode di atas menjadi ini:

 cat - > $file

Anda tidak memerlukan bit truncate, ini akan memotong dan menulis seluruh aliran STDIN untuk itu.

Sunting: Jika Anda menggunakan zsh, Anda dapat menggunakannya > $filesebagai pengganti kucing. Anda mengarahkan ke file dan memotongnya, tetapi jika ada sesuatu yang menunggu sesuatu untuk menerima STDIN itu akan dibaca pada saat itu. Saya pikir Anda dapat melakukan sesuatu seperti ini dengan bash tetapi Anda harus mengatur beberapa mode khusus.

Caleb
sumber
Saya tidak bisa membuat contoh pengalihan stdin berfungsi, tetapi mengubah contoh kucing menjadi> | (Saya punya set noclobber) bekerja seperti pesona. Terima kasih telah membuat hari saya ^. ^
David Souther
+1 untuk versi tanpa kucing. Selalu menghindari kucing yang tidak berguna;)
rozcietrzewiacz
@rozcietrzewiacz: Benar, kecuali itu hanya renungan dan saya salah. Ini mungkin bukan penggunaan kucing yang tidak berguna. Satu-satunya hal yang mungkin dapat Anda lakukan adalah > $file. Ini hanya berfungsi sebagai hal pertama yang mencari stdin di skrip shell induk. Pada dasarnya semua kode David dapat direduksi menjadi satu karakter, tetapi saya pikir cat -ini lebih elegan dan lebih sedikit masalah karena dipahami pada pandangan.
Caleb
Kadang-kadang saya merangkai empat atau lima catbersama-sama, hanya untuk mengganggu fanatik UUOC
Michael Mrozek
@MichaelMrozek: Terkadang saya memberi nama file data saya catagar orang yang bersikeras menggunakannya harus melakukan senam mental untuk membaca kode. Pipa yang diberi nama juga merupakan target yang baik.
Caleb
7

Untuk membaca file teks secara harfiah, jangan gunakan file biasa read, yang memproses output dengan dua cara:

  • readmenafsirkan \sebagai karakter pelarian; gunakan read -runtuk mematikan ini.
  • readterbagi menjadi kata-kata pada karakter dalam $IFS; setel IFSke string kosong untuk mematikannya.

Idiom biasa untuk memproses file teks baris demi baris adalah

while IFS= read -r line; do 

Untuk penjelasan tentang idiom ini, lihat Mengapa while IFS= readsering digunakan, bukan IFS=; while read..? .

Untuk menulis string secara harfiah, jangan hanya menggunakan plain echo, yang memproses string dengan dua cara:

  • Pada beberapa shell, echoproses backslash lolos. (Pada bash, itu tergantung apakah xpg_echoopsi diatur.)
  • Beberapa string diperlakukan sebagai opsi, misalnya -natau -e(set yang tepat tergantung pada shell).

Cara portabel untuk mencetak string adalah dengan printf. (Tidak ada cara yang lebih baik dalam bash, kecuali Anda tahu input Anda tidak terlihat seperti opsi untuk echo.) Gunakan formulir pertama untuk mencetak string yang tepat, dan bentuk kedua jika Anda ingin menambahkan baris baru.

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

Ini hanya cocok untuk memproses teks , karena:

  • Sebagian besar shell akan tersedak karakter null di input.
  • Ketika Anda membaca baris terakhir, Anda tidak memiliki cara untuk mengetahui apakah ada baris baru di akhir atau tidak. (Beberapa shell yang lebih tua mungkin memiliki masalah lebih besar jika input tidak diakhiri dengan baris baru.)

Anda tidak dapat memproses data biner di shell, tetapi versi utilitas modern di sebagian besar unix dapat mengatasi data yang berubah-ubah. Untuk meneruskan semua input ke output, gunakan cat. Bersinggungan, echo -n ''adalah cara yang rumit dan tidak portabel dalam melakukan apa pun; echo -nakan sama baiknya (atau tidak tergantung pada shell), dan :lebih sederhana dan sepenuhnya portabel.

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

atau, lebih sederhana,

cat >|"$file"

Dalam skrip, Anda biasanya tidak perlu menggunakan >|karena noclobberdinonaktifkan secara default.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
terima kasih telah menunjukkan xpg_echo, itu sebenarnya masalah yang saya alami di tempat lain dalam kode saya dan bahkan tidak menyadarinya. Re noclobber, saya memiliki kebiasaan menyalakannya di bashrc saya.
David Souther
0

Ini akan melakukan apa yang Anda inginkan:

( 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}

Perhatikan penggunaan memori. Ini membaca input dengan cara tanpa batas nol.

Jika tidak ada byte \0 nol dalam input maka bash pertama-tama harus membaca seluruh isi input ke dalam memori, dan kemudian mengeluarkannya.

Mengenai langkah terpotong Anda:

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

yang jauh lebih sederhana dan setara adalah:

> ${file}   #Truncate the file
Marc Tamsky
sumber