zsh: Membaca dari stdin hingga pemisah string

Aug 31 2020

Saya ingin membaca stdinsampai pemisah string MARKER=$'\0'"BRISH_MARKER" ditemukan. Saya mencoba:

❯ unset br ; print -rn -- hi${MARKER}world |   {  IFS= read -d "$MARKER" -r br ; cat -v } ; echo ; typeset -p br

Pemberian yang mana:

BRISH_MARKERworld
typeset br=hi

Jadi, readhanya menggunakan karakter pertama dari pembatas yang diberikan \0,. Saya ingin menggunakan seluruh string. Bagaimana saya bisa mencapai ini?

Masalah yang saya coba selesaikan adalah saya memiliki proses yang terus menerus memasukkan aliran data ke proses zsh, dan data perlu dipecah menjadi nilai yang berbeda menggunakan pembatas. Saya awalnya menggunakan just \0, tetapi itu tidak memungkinkan saya untuk menggunakan nilai yang mengandung \0, jadi saya mencoba menggunakan arus MARKER.

Jawaban

2 StéphaneChazelas Sep 01 2020 at 16:45

Ya read -dhanya bekerja dengan pembatas satu karakter (dalam bashdan ksh93, hanya bekerja dengan pembatas byte tunggal ).

Membaca hingga pembatas juga berarti Anda perlu membaca satu byte pada satu waktu (terutama dengan input yang tidak dapat dicari seperti pipa) untuk memastikan Anda tidak membaca melewati pembatas, yang membuatnya tidak efisien.

Saya sarankan menggunakan length:valuecatatan sebagai gantinya:

write-record() {
  set -o localoptions +o multibyte
  print -rn -- "$#1:$1"
}
read-record() {
  set -o localoptions +o multibyte
  local len

  # note that in current versions of zsh, read -k0 (for empty records)
  # returns a non-zero exit status.
  eval "$1=" IFS= read -rd: len || return ((len == 0)) || read -u0 -k "$len" "$1"
}

(menonaktifkan multibytesecara lokal untuk bekerja dengan panjang dalam byte dan menghindari pengkodean / dekode karakter yang tidak berguna di sini).

Kemudian sebagai contoh:

$ (write-record $'é\0x'; write-record $'foo\0MARKER') | { read-record a && read-record b; printf "<%q>\n" "$a" "$b"; }
<é$'\0'x> <foo$'\0'MARKER>