Saya ambisius mencoba menerjemahkan kode c ++ ke bash karena berbagai alasan.
Kode ini membaca dan memanipulasi tipe file khusus untuk sub-bidang saya yang ditulis dan terstruktur sepenuhnya dalam biner. Tugas saya yang berhubungan dengan biner pertama adalah menyalin 988 byte pertama dari header, persis apa adanya, dan memasukkannya ke dalam file output yang saya dapat terus menulis ketika saya menghasilkan sisa informasi.
Saya cukup yakin bahwa solusi saya saat ini tidak berfungsi, dan secara realistis saya belum menemukan cara yang baik untuk menentukan ini. Jadi, bahkan jika itu benar-benar ditulis dengan benar, saya perlu tahu bagaimana saya akan menguji ini untuk memastikan!
Inilah yang sedang saya lakukan sekarang:
hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly. exiting. please troubleshoot."; exit 1; fi
Jika saya menggunakan hexdump / xxd untuk memeriksa bagian file ini, walaupun saya tidak bisa membaca sebagian besar, ada sesuatu yang salah. Dan kode yang saya tulis sebagai perbandingan hanya memberi tahu saya jika dua string identik, bukan jika mereka disalin seperti yang saya inginkan.
Apakah ada cara yang lebih baik untuk melakukan ini di bash? Bisakah saya cukup menyalin / membaca byte biner dalam native-binary, untuk menyalin ke file kata demi kata? (dan idealnya untuk menyimpan sebagai variabel juga).
dd
untuk menyalin masing-masing byte (mengaturnyacount
untuk1
). Saya tidak yakin tentang menyimpannya.Jawaban:
Berurusan dengan data biner pada level rendah dalam skrip shell umumnya merupakan ide yang buruk.
bash
variabel tidak dapat berisi byte 0.zsh
adalah satu-satunya shell yang dapat menyimpan byte itu dalam variabelnya.Dalam kasus apa pun, argumen perintah dan variabel lingkungan tidak dapat berisi byte tersebut karena mereka adalah string yang dibatasi NUL yang diteruskan ke
execve
panggilan sistem.Perhatikan juga bahwa:
atau bentuknya yang modern:
strip semua karakter trailing baris baru dari output dari
cmd
. Jadi, jika output biner itu berakhir dalam 0xa byte, itu akan hancur ketika disimpan di$var
.Di sini, Anda perlu menyimpan data yang disandikan, misalnya dengan
xxd -p
.Anda dapat mendefinisikan fungsi pembantu seperti:
xxd -p
output tidak ruang efisien karena mengkodekan 1 byte dalam 2 byte, tetapi membuatnya lebih mudah untuk melakukan manipulasi dengannya (menyatukan, mengekstraksi bagian).base64
adalah salah satu yang mengkodekan 3 byte dalam 4, tetapi tidak mudah untuk dikerjakan.The
ksh93
shell memiliki builtin encoding format yang (kegunaanbase64
) yang dapat Anda gunakan dengan nyaread
danprintf
/print
utilitas:Sekarang, jika tidak ada transit melalui variabel shell atau env, atau argumen perintah, Anda harus OK selama utilitas yang Anda gunakan dapat menangani nilai byte apa pun. Tetapi perhatikan bahwa untuk utilitas teks, sebagian besar implementasi non-GNU tidak dapat menangani byte NUL, dan Anda harus memperbaiki lokal ke C untuk menghindari masalah dengan karakter multi-byte. Karakter terakhir yang tidak menjadi karakter baris baru juga dapat menyebabkan masalah dan juga garis yang sangat panjang (urutan byte di antara dua 0xa byte yang lebih panjang
LINE_MAX
).head -c
di mana itu tersedia harus OK di sini, karena itu dimaksudkan untuk bekerja dengan byte, dan tidak memiliki alasan untuk memperlakukan data sebagai teks. Begituharus baik-baik saja. Dalam praktiknya setidaknya implementasi builtin GNU, FreeBSD dan ksh93 adalah OK. POSIX tidak menentukan
-c
opsi, tetapi mengatakanhead
harus mendukung garis dengan panjang berapa pun (tidak terbatas padaLINE_MAX
)Dengan
zsh
:Atau:
Bahkan di
zsh
, jika$var
berisi byte NUL, Anda bisa meneruskannya sebagai argumen kezsh
builtin (seperti diprint
atas) atau fungsi, tetapi bukan sebagai argumen untuk executable, karena argumen yang diteruskan ke executable adalah string dibatasi NUL, itu adalah batasan kernel, tidak tergantung pada shell.sumber
zsh
bukan satu-satunya shell yang dapat menyimpan satu atau lebih byte NUL dalam variabel shell.ksh93
dapat melakukannya juga. Secara internal,ksh93
cukup simpan variabel biner sebagai string yang dikodekan base64.Baiklah. Tapi mungkin Anda harus mempertimbangkan alasan yang sangat penting untuk TIDAK melakukannya. Pada dasarnya, "bash" / "sh" / "csh" / "ksh" dan sejenisnya tidak dirancang untuk memproses data biner, dan tidak juga sebagian besar utilitas standar UNIX / LINUX.
Anda akan lebih baik tetap dengan C ++, atau menggunakan bahasa scripting seperti Python, Ruby atau Perl yang mampu menangani data biner.
Cara yang lebih baik adalah tidak melakukannya di bash.
sumber
ffmpeg
,imagemagick
,dd
). Sekarang jika seseorang melakukan pemrograman daripada menempelkan semuanya bersama-sama maka menggunakan bahasa pemrograman yang berdaya penuh adalah cara yang harus dilakukan.Dari pertanyaan Anda:
Jika Anda menyalin 988 baris, maka sepertinya file teks, bukan biner. Namun, kode Anda tampaknya mengasumsikan 988 byte, bukan 988 baris, jadi saya akan menganggap byte benar.
Bagian ini mungkin tidak berfungsi. Untuk satu hal, setiap byte NUL dalam aliran akan dilucuti, karena Anda gunakan
${hdr_988}
sebagai argumen baris perintah, dan argumen baris perintah tidak dapat berisi NUL. Backticks mungkin melakukan greening spasi putih juga (saya tidak yakin tentang itu). (Sebenarnya, karenaecho
ini adalah built-in, pembatasan NUL mungkin tidak berlaku, tapi saya akan mengatakan itu masih rapuh.)Mengapa tidak menulis header langsung dari file input ke file output, tanpa melewati variabel shell?
Atau, lebih nyaman,
Karena Anda menyebutkan Anda menggunakan
bash
, bukan shell POSIX, Anda memiliki substitusi proses yang tersedia untuk Anda, jadi bagaimana dengan ini sebagai tes?Akhirnya: pertimbangkan untuk menggunakan
$( ... )
backticks.sumber
dd
belum tentu setara denganhead
untuk file non-reguler.head
akan melakukanread(2)
panggilan sistem sebanyak yang diperlukan untuk mendapatkan 988 byte tersebut sementaradd
hanya akan melakukan saturead(2)
. GNUdd
memilikiiflag=fullblock
untuk mencoba dan membaca blok itu secara penuh, tetapi itu bahkan lebih portabel daripadahead -c
.