Z wielu powodów staram się tłumaczyć kod c ++ na bash.
Ten kod odczytuje i manipuluje typem pliku specyficznym dla mojego subpola, który jest zapisany i ustrukturyzowany całkowicie w formacie binarnym. Moim pierwszym zadaniem związanym z plikami binarnymi jest skopiowanie pierwszych 988 bajtów nagłówka, dokładnie tak, jak jest, i umieszczenie ich w pliku wyjściowym, do którego mogę kontynuować zapisywanie podczas generowania reszty informacji.
Jestem całkiem pewien, że moje obecne rozwiązanie nie działa, i realistycznie nie znalazłem dobrego sposobu, aby to ustalić. Więc nawet jeśli jest napisane poprawnie, muszę wiedzieć, jak bym to przetestował, aby się upewnić!
Oto co teraz robię:
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
Jeśli użyję hexdump / xxd do sprawdzenia tej części pliku, chociaż nie mogę dokładnie odczytać większości tego pliku, coś wydaje się nie tak. A kod, który napisałem dla porównania, mówi mi tylko, czy dwa ciągi są identyczne, a nie, jeśli są kopiowane tak, jak chcę.
Czy jest lepszy sposób na zrobienie tego w bash? Czy mogę po prostu skopiować / odczytać bajty binarne w natywnym pliku binarnym, aby skopiować je do pliku dosłownie? (i idealnie, aby przechowywać również jako zmienne).
dd
do skopiowania pojedynczych bajtów (ustawiająccount
na1
). Jednak nie jestem pewien, czy je przechowywać.Odpowiedzi:
Radzenie sobie z danymi binarnymi na niskim poziomie w skryptach powłoki jest ogólnie złym pomysłem.
bash
zmienne nie mogą zawierać bajtu 0.zsh
jest jedyną powłoką, która może przechowywać ten bajt w swoich zmiennych.W każdym razie argumenty poleceń i zmienne środowiskowe nie mogą zawierać tych bajtów, ponieważ są one łańcuchami rozdzielanymi NUL przekazywanymi do
execve
wywołania systemowego.Pamiętaj również, że:
lub jego nowoczesna forma:
usuwa wszystkie końcowe znaki nowej linii z wyniku
cmd
. Jeśli więc to wyjście binarne kończy się na bajty 0xa, zostanie ono zniekształcone, gdy zostanie zapisane$var
.W tym miejscu należy przechowywać zakodowane dane, na przykład za pomocą
xxd -p
.Możesz zdefiniować funkcje pomocnicze, takie jak:
xxd -p
dane wyjściowe nie zajmują mało miejsca, ponieważ kodują 1 bajt na 2 bajty, ale ułatwiają manipulowanie nim (łączenie, wydobywanie części).base64
to taki, który koduje 3 bajty na 4, ale nie jest tak łatwy w obsłudze.ksh93
Powłoka ma polecenie wbudowane kodujący Format (zastosowańbase64
), które można wykorzystać z jegoread
iprintf
/print
media:Teraz, jeśli nie ma tranzytu przez zmienne powłoki lub env lub argumenty poleceń, powinieneś być OK, o ile używane narzędzia mogą obsłużyć dowolną wartość bajtu. Należy jednak pamiętać, że w przypadku narzędzi tekstowych większość implementacji innych niż GNU nie może obsługiwać bajtów NUL, a będziesz chciał ustawić ustawienia regionalne na C, aby uniknąć problemów ze znakami wielobajtowymi. Ostatni znak, który nie jest znakiem nowej linii, może również powodować problemy, a także bardzo długie linie (sekwencje bajtów między dwoma bajtami 0xa, które są dłuższe
LINE_MAX
).head -c
tam, gdzie jest dostępny, powinno być tutaj OK, ponieważ ma pracować z bajtami i nie ma powodu, aby traktować dane jako tekst. WięcPowinno być ok. W praktyce przynajmniej wbudowane implementacje GNU, FreeBSD i ksh93 są w porządku. POSIX nie określa
-c
opcji, ale mówi, żehead
powinien obsługiwać linie dowolnej długości (nie ograniczone doLINE_MAX
)Z
zsh
:Lub:
Nawet wewnątrz
zsh
, jeśli$var
zawiera bajty NUL, możesz przekazać go jako argument dozsh
wbudowanych funkcji (jakprint
wyżej) lub funkcji, ale nie jako argumentów do plików wykonywalnych, ponieważ argumenty przekazywane do plików wykonywalnych są łańcuchami ograniczonymi przez NUL, jest to ograniczenie jądra niezależne od powłoki.źródło
zsh
nie jest jedyną powłoką, która może przechowywać jeden lub więcej bajtów NUL w zmiennej powłoki.ksh93
mogę to również zrobić. Wewnętrznieksh93
po prostu przechowuje zmienną binarną jako ciąg zakodowany w standardzie base64.No tak. Ale może powinieneś rozważyć bardzo ważny powód, aby tego NIE robić. Zasadniczo „bash” / „sh” / „csh” / „ksh” i tym podobne nie są przeznaczone do przetwarzania danych binarnych i nie są większością standardowych narzędzi UNIX / LINUX.
Lepiej byłoby trzymać się C ++ lub użyć języka skryptowego takiego jak Python, Ruby lub Perl, który jest w stanie radzić sobie z danymi binarnymi.
Lepszym sposobem jest nie robić tego bash.
źródło
ffmpeg
,imagemagick
,dd
). Teraz, jeśli ktoś programuje zamiast sklejać rzeczy ze sobą, to najlepiej jest użyć języka programowania o pełnej mocy.Z twojego pytania:
Jeśli kopiujesz 988 linii, wygląda to na plik tekstowy, a nie binarny. Jednak twój kod wydaje się przyjmować 988 bajtów, a nie 988 linii, więc założę, że bajty są poprawne.
Ta część może nie działać. Po pierwsze, wszystkie bajty NUL w strumieniu zostaną usunięte, ponieważ używasz ich
${hdr_988}
jako argumentu wiersza poleceń, a argumenty wiersza poleceń nie mogą zawierać wartości NUL. Backticks również może wykonywać munging spacjami (nie jestem tego pewien). (W rzeczywistości, ponieważecho
jest to wbudowane ograniczenie NUL może nie mieć zastosowania, ale powiedziałbym, że nadal jest niepewne.)Dlaczego nie napisać nagłówka bezpośrednio z pliku wejściowego do pliku wyjściowego, bez przekazywania go przez zmienną powłoki?
Lub, bardziej przenośnie,
Ponieważ wspomniałeś, że używasz
bash
, a nie powłoki POSIX, masz do dyspozycji substytucję procesu, więc co powiesz na ten test?Wreszcie: rozważ użycie
$( ... )
zamiast backticks.źródło
dd
niekoniecznie jest to równoważne zhead
plikami nieregularnymi.head
wykona tyleread(2)
wywołań systemowych, ile potrzeba, aby uzyskać 988 bajtów, podczas gdydd
zrobi tylko jednoread(2)
. GNUdd
musiiflag=fullblock
spróbować odczytać ten blok w całości, ale jest to nawet mniej przenośne niżhead -c
.