Jak przechowywać plik obrazu w zmiennej bash?

14

Po użyciu następującego polecenia

pngString="$(cat example.png)"    
echo -n "$pngString" > tmp.png

Nie mogę otworzyć pliku tmp.png jako pliku PNG. Być może niektóre informacje są tracone, gdy używam $pngStringdo przechowywania pliku obrazu.

Pytanie brzmi: w jaki sposób mogę przechowywać pełne informacje o obrazie, używając zmiennej w skrypcie bash?

Libin Wen
źródło
Polecenie cat działa dobrze (do przechowywania danych obrazu w zmiennej). Wystarczy usunąć podwójne cudzysłowy. Pytanie brzmi: co chcesz zrobić z tymi danymi po zapisaniu ich w zmiennej?
coffeMug
5
cati echowszystkie podobne są narzędziami tekstowymi . Pozbawienie ich niczego nie podejrzewających plików binarnych przyniesie nieprzewidziane skutki. Właśnie dlatego base64wymyślono takie rzeczy .
Shadur
Dlaczego chcesz mieć to jako zmienną? Zmienne powłoki są zwykle przeznaczone do przechowywania niewielkiej ilości tekstu, a nie dużych ilości danych binarnych. Żadna ze zwykłych powłok nie radzi sobie z danymi binarnymi w zmiennych (dławią się na bajtach zerowych) oprócz zsh.
Gilles „SO - przestań być zły”
Dlaczego jeszcze nie tylko cat example.png > tmp.pnglub lepiej cp example.png tmp.png?
Wildcard
@Shadur, cattak naprawdę nie jest narzędziem tekstowym. To podstawienie polecenia (które usuwa końcowe znaki nowego wiersza), przypisanie zmiennej (która nie może zawierać bajtów pustych) i echopolecenie (które może interpretować sekwencje odwrotnego ukośnika), które nie będą miały wpływu na układ binarny cat. Ale zgadzam się z twoim ogólnym punktem.
Wildcard

Odpowiedzi:

20

Masz rację, a echofirma nie radzi sobie z binarnością tak dobrze. Podejrzewam, że znaki zerowe zbyt wcześnie przerywają strumień.

Możesz konwertować informacje o obrazie w pewnym formacie opartym na ASCII. Na przykład dotyczy to base64:

$ pic=`base64 pic.jpeg`
$ echo $pic | base64 --decode > pic2.jpeg
$ diff pic*
$ echo $?
0
nperson325681
źródło
3
W moim systemie bash musiałem zawrzeć zmienną w cudzysłowie, prawdopodobnie dlatego, że base64 może zawierać białe echo "$pic" | base64 --decode > pic2.jpeg
znaki
1

Problem polega na tym, że bajty zerowe nie mogą być przekazywane przez argumenty wiersza poleceń, ponieważ są one wewnętrznie używane jako terminatory argumentów. Wszystkie pozostałe bajty wydają się być w porządku. Tak więc nieco bardziej wydajną przestrzennie (zazwyczaj) alternatywą jest użycie base64ucieczki przed pustymi bajtami, a następnie użycie printfdo konwersji danych do oryginalnej postaci:

pngString="$(sed 's/\\/\\\\/g;s/%/%%/g;s/\x00/\\x00/g' <example.png)"
printf "$pngString" >tmp.png

\I %znaki mają specjalne do printftak muszą być uciekł zbyt.

Zauważ również, że jeśli dane wejściowe kończą się znakiem nowej linii, zostaną usunięte przez zastąpienie polecenia. Nie powinno to stanowić problemu szczególnie w przypadku plików PNG, ponieważ ostatni bajt w prawidłowym pliku PNG powinien mieć wartość 0x82, najmniej znaczący bajt w IENDsumie CRC pustej porcji.

Karel Vlk
źródło
1
Jeśli chcesz jeszcze więcej odwrotnych ukośników, użyj: sed s/\\\\/\\\\\\\\/g\;s/%/%%/g\;s/\\x00/\\\\x00/g
Karel Vlk