Herhangi bir dönüşüm olmadan bayt kelimesi kelimesini kopyalamak için bash'daki ikili ile nasıl çalışabilirim?


14

Hırslı bir şekilde sayısız nedenden ötürü bash içine bir c ++ kodu çevirmek çalışıyorum.

Bu kod, alt alanıma özgü, tamamen ikili olarak yazılmış ve yapılandırılmış bir dosya türünü okur ve işler. İlk ikili ile ilgili görevim, başlığın ilk 988 baytını aynen olduğu gibi kopyalamak ve geri kalan bilgileri oluştururken yazmaya devam edebileceğim bir çıktı dosyasına koymak.

Mevcut çözümümün çalışmadığından eminim ve gerçekçi olarak bunu belirlemek için iyi bir yol bulamadım. Bu yüzden gerçekten doğru yazılmış olsa bile, emin olmak için bunu nasıl test edeceğimi bilmeliyim!

Şu anda yaptığım şey bu:

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

Dosyanın bu kısmını kontrol etmek için hexdump / xxd kullanırsam, çoğunu tam olarak okuyamıyorum, bir şey yanlış görünüyor. Ve karşılaştırma için yazdığım kod bana sadece iki dizenin aynı olup olmadığını, onların olmasını istediğim şekilde kopyalandıklarını söyler.

Bunu bash'da yapmanın daha iyi bir yolu var mı? Bir dosyaya kopyalamak için yerel ikili dosyadaki ikili baytları kopyalayabilir / okuyabilir miyim? (ve ideal olarak değişken olarak depolamak için).


Sen kullanabilirsiniz dd(onun ayarı tek tek bayt kopyalamak için countTo 1). Yine de onları sakladığımdan emin değilim.
DDPWNAGE

C yolunda bash yapmayın, birçok baş ağrısı yaratacaktır. Bunun yerine uygun bash yapılarını kullanın
Ferrybig

Yanıtlar:


22

Kabuk komut dosyalarında düşük düzeyde ikili verilerle uğraşmak genellikle kötü bir fikirdir.

bashdeğişkenler bayt 0'ı içeremez. zshBu baytı değişkenlerinde saklayabilen tek kabuktur.

Her durumda, komut bağımsız değişkenleri ve ortam değişkenleri, execvesistem çağrısına iletilen NUL sınırlandırılmış dizeler oldukları için bu baytları içeremez .

Ayrıca şunu da unutmayın:

var=`cmd`

ya da modern biçimi:

var=$(cmd)

çıkışındaki tüm satırsonu karakterlerini çıkarır cmd. Dolayısıyla, bu ikili çıktı 0xa bayt ile biterse, depolandığında karıştırılır $var.

Burada, örneğin ile kodlanmış verileri depolamanız gerekir xxd -p.

hdr_988=$(head -c 988 < "$inputFile" | xxd -p)
printf '%s\n' "$hdr_988" | xxd -p -r > "$output_hdr"

Yardımcı fonksiyonları şöyle tanımlayabilirsiniz:

encode() {
  eval "$1"='$(
    shift
    "$@" | xxd -p  -c 0x7fffffff
    exit "${PIPESTATUS[0]}")'
}

decode() {
  printf %s "$1" | xxd -p -r
}

encode var cat /bin/ls &&
  decode "$var" | cmp - /bin/ls && echo OK

xxd -pçıktı, 2 baytta 1 baytı kodladığı için alandan tasarruflu değildir, ancak onunla manipülasyonlar yapmayı kolaylaştırır (birleştirme, parçaları çıkarma). base644'te 3 baytı kodlayan, ancak çalışması kolay olmayan bir bayttır.

ksh93Kabuk biçimi (kullanımları kodlayan bir yerleşiği olan base64, onun ile kullanılır) readve printf/ printkamu:

typeset -b var # marked as "binary"/"base64-encoded"
IFS= read -rn 988 var < input
printf %B var > output

Kabuk veya env değişkenleri veya komut bağımsız değişkenleri üzerinden geçiş yoksa, kullandığınız yardımcı programlar bayt değerini işleyebildiği sürece Tamam olmalısınız. Ancak, metin yardımcı programları için, GNU olmayan uygulamaların çoğunun NUL baytlarını işleyemeyeceğini ve çok baytlık karakterlerle ilgili sorunları önlemek için yerel ayarı C olarak düzeltmek isteyeceğinizi unutmayın. Son satır, yeni satır karakteri olmamakla birlikte sorunların yanı sıra çok uzun satırlara da neden olabilir (iki 0xa bayt arasındaki bayt dizileri daha uzun LINE_MAX).

head -cburada bayt ile çalışmak anlamına geldiğinden ve verileri metin olarak ele almak için bir neden olmadığından burada uygun olmalıdır. Yani

head -c 988 < input > output

iyi olmalı. Uygulamada en azından GNU, FreeBSD ve ksh93 yerleşik uygulamaları sorun oluşturmaz. POSIX -cseçeneği belirtmez , ancak headherhangi bir uzunluktaki satırları desteklemesi gerektiğini söylüyor (sınırlı değildir LINE_MAX)

İle zsh:

IFS= read -rk988 -u0 var < input &&
print -rn -- $var > output

Veya:

var=$(head -c 988 < input && echo .) && var=${var%.}
print -rn -- $var > output

Hatta içinde zshise, $varNUL bayt içeren, size argüman olarak geçebilir zshbuiltins (gibi printyukarıda) veya fonksiyonlar, ancak yürütülebilir argümanlarla gibi NUL kabuğun bağımsız bir çekirdek sınırlaması olduğunu, dizeleri ayrılmış, yürütülebilir argüman olarak.


zshbir kabuk değişkeninde bir veya daha fazla NUL baytını depolayabilen tek kabuk değildir. ksh93bunu da yapabilir. Dahili olarak, ksh93binary değişkeni bir base64 kodlu dize olarak saklar.
fpmurphy

@ fpmurphy1, ikili veri işleme dediğim bu değil, değişken ikili veri içermiyor, bu nedenle üzerlerindeki kabuk operatörlerinden hiçbirini kullanamazsınız, bunları yerleşik yapılara veya işlevlere aktaramazsınız. çözülmüş formu ... Ben daha çok yerleşik base64 kodlama / kod çözme desteği diyorum .
Stéphane Chazelas

11

Hırslı bir şekilde sayısız nedenden ötürü bash içine bir c ++ kodu çevirmek çalışıyorum.

İyi evet. Ama belki de YAPMAMAK için çok önemli bir neden düşünmelisiniz. Temel olarak, "bash" / "sh" / "csh" / "ksh" ve benzerleri ikili verileri işlemek için tasarlanmamıştır ve ikisi de standart UNIX / LINUX yardımcı programlarının çoğu değildir.

C ++ ile yapışmadan veya ikili verilerle başa çıkabilen Python, Ruby veya Perl gibi komut dosyası dilini kullanmanız daha iyi olur.

Bunu bash'da yapmanın daha iyi bir yolu var mı?

Daha iyi yol bash'de yapmamaktır.


4
+1 için "En iyi yol bunu bash'da yapmamaktır."
Guntram Blohm Monica'yı

1
Bu rotaya gitmemenin bir başka nedeni, sonuçta ortaya çıkan uygulamanın önemli ölçüde daha yavaş çalışması ve daha fazla sistem kaynağı tüketmesidir.
fpmurphy

Bash boru hatları, anlaşılabilirliği artırabilecek yüksek düzeyde alana özgü bir tür dil olarak işlev görebilir. İkili olmayan bir boru hattı ile ilgili bir şey yok, ve komut satırı araçları olarak uygulanan çeşitli yardımcı programlar vardır ikili verilerle etkileşim ( ffmpeg, imagemagick, dd). Şimdi, bir şeyleri bir araya getirmek yerine programlama yapıyorsa , tam güçlü bir programlama dili kullanmak gidilecek yoldur.
Att Righ

6

Sorunuzdan:

başlığın ilk 988 satırını kopyala

988 satır kopyalıyorsanız, ikili değil, bir metin dosyası gibi görünür. Ancak, kod 988 satır değil 988 bayt varsayıyor gibi görünüyor, bu yüzden bayt doğru olduğunu varsayacağım.

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}

Bu bölüm çalışmayabilir. Birincisi, ${hdr_988}komut satırı bağımsız değişkeni olarak kullandığınız ve komut satırı bağımsız değişkenleri NUL içeremediği için akıştaki herhangi bir NUL baytı çıkarılır . Backticks de boşluk munging yapıyor olabilir (bundan emin değilim). (Aslında, echoyerleşik olduğundan, NUL kısıtlaması geçerli olmayabilir , ancak yine de iffy olduğunu söyleyebilirim.)

Neden üstbilgiyi bir kabuk değişkeninden geçirmeden doğrudan giriş dosyasından çıktı dosyasına yazmıyorsunuz?

head -c 988 "${inputFile}" >"${output_hdr}"

Veya daha taşınabilir bir şekilde,

dd if="${inputFile}" of="${output_hdr}" bs=988 count=1

bashPOSIX kabuğunu değil , kullandığınızdan bahsettiğinizden , kullanabileceğiniz işlem ikamesiniz var, peki buna bir test olarak ne dersiniz?

cmp <(head -c 988 "${inputFile}") <(head -c 988 "${output_hdr}")

Son olarak: ters tırnak yerine kullanmayı düşünün$( ... ) .


Bunun normal olmayan dosyalar için ddmutlaka eşdeğer olmadığını unutmayın head. bu 988 baytı almak için gereken headsayıda read(2)sistem çağrısı yaparken ddsadece bir tane yapacak read(2). GNU ddbir sahiptir iflag=fullblockdenemek ve tam olarak bu bloğu okumak, ama bu durumda bile daha az taşınabilir head -c.
Stéphane Chazelas
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.