初歩のシェルスクリプトで遊ぶ[シフトJISをutf8に変換-3,メガネハナーンを6226個に切り刻む]

調べものには使えません。
Ubuntu18.04,Virtualbox6.1.2,dash,bash

端末の平仮名をバラす

$ echo -n あ | od -An -tx1
 e3 81 82

「あ」をechoし、odに読ませると、「e3 81 82」と出ます。

$ echo -n あ 1>あ.txt
$ 0< あ.txt od -tx1
0000000 e3 81 82
0000003

「あ」をファイルにリダイレクトして、ファイルをodで読んでも、同じく「e3 81 82」。普通にエディタでも「あ」と読める、正しいテキストファイルが作れる。

$ cat あ.txt | head --bytes=1 > e3.file
$ 0< e3.file od -tx1
0000000 e3
0000001

「あ.txt」の1バイト目だけheadコマンドで切り取り、ファイルを作ると、「e3」だけになった。

$ 0< あ.txt  head --bytes=2 | tail --bytes=1 > 81.file
$ 0< あ.txt  head --bytes=3 | tail --bytes=1 > 82.file

同様に2バイト目、3バイト目をファイルにして、

$ cat e3.file 81.file 82.file 
あ 

「e3 81 82」の順にcatすると、「あ」と表示された。(実際に端末で実行すると改行なしで表示される)
catコマンドで「あ」と表示するには、「e3 81 82」と書かれた3バイトのファイルでなくてもよい。
「e3」と「81」と「82」の3ファイルでも構わない。

次に、同じように「や」でファイルを作る。「e3 82 84」だから、重複しない「84」を。

$ echo -n や  > や.txt
$ echo -n や | head --bytes=3 | tail --bytes=1 > 84.file
$ ls
81.file  82.file  84.file  e3.file  あ.txt  や.txt

元は「あ」と「や」だったファイルを、違う組み合わせでcatする、と?

$ cat e3.file 81.file 84.file 
い
$ cat e3.file 82.file 81.file 
め
$ cat e3.file 82.file 82.file 
も
$ cat e3.file 81.file 81.file 
ぁ
$ cat e3.file 81.file 82.file e3.file 82.file 84.file e3.file 81.file 84.file >あやい.txt
$ cat あやい.txt 
あやい
$ cat あやい.txt | od -tx1
0000000 e3 81 82 e3 82 84 e3 81 84
0000011

元の文字とは違う文字を、作ることができる。もちろんファイルにリダイレクトすることもできる。同じバイトを複数使っても構わない。

ここまでで、ごく小さなテキストファイルなら、catコマンドは1バイト単位で作れる、と分かった。
私はこんな使い方が想定されてるかなんて知りません。膨大な数はダメだとか、特定の文字しか作れないとか、環境依存とか、私には分かりませんが。


しかし、この前に7000個のファイルをcatコマンドで繋げ続けて、『ガリバー旅行記』を作ってみても、テキストエディタで作った正しいファイルとmd5が一致する、ということまでは分かってたわけです。

catコマンドは、人間が読むテキストファイルを連結したり、何ギガバイトもある大きなファイルを結合する、というもんだと思ってたのだけど。1バイト単位でも、やらせればできる、ってことは分かった。
catコマンドが悲鳴あげてるかもしれんけど。

256個のファイルがあれば、どんなファイルでも作れる?

「1バイトのファイル」は、多くても256種類あればいい。00〜FFまでなのだから。
もちろん全部を使える保証なんて無い。「00」だけ使えないとか、ありそうだ。しかしテキストファイルなら不要なバイトもあるだろう。7800個の辞書ファイルは、数を減らせるはず。

もし256種類使えたら?
テキストファイルだけでなく、「任意のファイルを作れるんじゃないの?」と考えたくなります。

1バイトのファイルをどうやって作るか

もちろん手段はいろいろ有るに決まっているのだけれど、私は他の言語も含めて知りません。xxdコマンドも知らなかった。echoやprintfでバイナリを作るのも、まだ調べてもいない。
Windowsバイナリエディタか何かを使うことも検討したけど、自分ができる範囲で、シェルスクリプトで考えたかった。

このとき思いついたのは、「小さなファイルをcatで組み合わせて大きなファイルを作る」のだから、逆に「大きなファイルを分割して小さくすれば、1バイトになるんじゃないの?」というもの。
模範解答をネットで探すより、手段があるなら作ったほうが早い。

メガネハナーンを6226個に切り刻む

cp932のテキストファイルをodコマンドで調べると、222種類と、だいぶ足りません。1バイトファイルの材料にはなりませんが、逆に、256種類なくてもテキストファイルなら作れる可能性はある、とわかった。

10年前の、メガネハナーンの落書きがありました。6226バイトのPNG画像です。

↑(注:この画像、アップロードしたら容量増えました。万一試すなら、お手元の手ごろな画像ファイルつかってください。)

cat hanamegane.png | od -An -v -tx1 | tr " " "\n" | sort | uniq -c |wc

odコマンドで調べると、256種類揃っていた。メガネハナーンを材料にします。

0< splitFile.temp split --bytes=1 

要点はココ。splitコマンドは、1バイトの指定でも受け付けてくれた。
作ったスクリプトは以下。

byteCellGenerator_02.sh

#!/bin/sh
#set -x
: << '#--------------------------------------------------------------comment'

■1バイトファイル作成スクリプト

メガネハナーンを千切りにして、1バイトのファイルを256種類生成する。


byteCellGenerator_02_01(){ # 作業場所の確認、実行の確認
byteCellGenerator_02_02(){ # ファイルを1バイト1ファイルに切り刻む
byteCellGenerator_02_03(){ # 1バイトファイルの中身を調べて名前変更する,重複を削除する

#--------------------------------------------------------------comment
# 設定
byteFileOutDir="./byteCellTemp/"  # ファイルを置くディレクトリを指定する
sengiriFile="./hanamegane.png"	# 切り刻み元のファイルを指定。せいぜい7000バイト程度に!



byteCellGenerator_02_01(){ # 作業場所の確認、実行の確認

# 作成するファイルの置き場所
mkdir "${byteFileOutDir}" 2>/dev/null
if mkdir "${byteFileOutDir}check/" ;then		# 試しに作成先に、ディレクトリを作成してみる
rmdir "${byteFileOutDir}check/"
else											# 作れなかったら終了
echo 作成先ディレクトリを確認 ;exit 1
fi

# 切り刻むファイル
sengiriFileSize=`cat "${sengiriFile}" | wc -c`
echo "${sengiriFileSize}"byte
if test 0${sengiriFileSize} -lt 6500 ;then		#最大ファイルサイズの制限
:
else
echo '終了する'
exit
fi

# 実行確認
echo "${byteFileOutDir}:作成先ディレクトリ"
echo "${sengiriFileSize}:生成予定ファイル数"
echo '[y]実行 [*]終了'
read action
case $action in
( y )
true
;;
( * )
echo '終了する' ;exit 0
;;
esac
}


byteCellGenerator_02_02(){ # ファイルを1バイト1ファイルに切り刻む

cp "${sengiriFile}" "${byteFileOutDir}"/splitFile.temp
(
	cd "${byteFileOutDir}"
	# 「--bytes=1」は、まず「--bytes=1000」以上でテストすること
	0< splitFile.temp split --bytes=1 --numeric-suffixes=0 --suffix-length=6 --additional-suffix=".chip" --verbose
)
rm "${byteFileOutDir}"/splitFile.temp
if test ${?} -ne 0 ;then
exit
else true
fi
}


# 1バイトファイルの中身を調べて名前変更する,
# 同一内容のファイルを上書きしてゆき、256種類以下に減らす
byteCellGenerator_02_03(){
(
cd ${byteFileOutDir} # mvでリネームと上書きをするためにcdする
sengiriChipS="`echo *.chip`"

chipCount=`echo ${sengiriChipS} | wc -w `
echo ${chipCount}:ファイル数

# 実行確認
echo 'ファイルを検査、リネーム、重複削除'
echo '[y]実行 [*]終了'
read action
case $action in
( y )
true
;;
( * )
echo '終了する' ;exit 0
;;
esac

# 主処理
for chip in ${sengiriChipS} ;do
chipHex=`od -An -tx1 -v "${chip}"`
mv -v "${chip}" ${chipHex}
done
ls
echo `ls | wc -w`" 種類"
)
}


byteCellGenerator_02_01
byteCellGenerator_02_02
byteCellGenerator_02_03

exit 0
実行結果
renamed 'x006219.chip' -> '45'
renamed 'x006220.chip' -> '4e'
renamed 'x006221.chip' -> '44'
renamed 'x006222.chip' -> 'ae'
renamed 'x006223.chip' -> '42'
renamed 'x006224.chip' -> '60'
renamed 'x006225.chip' -> '82'
00  09	12  1b	24  2d	36  3f	48  51	5a  63	6c  75	7e  87	90  99	a2  ab	b4  bd	c6  cf	d8  e1	ea  f3	fc
01  0a	13  1c	25  2e	37  40	49  52	5b  64	6d  76	7f  88	91  9a	a3  ac	b5  be	c7  d0	d9  e2	eb  f4	fd
02  0b	14  1d	26  2f	38  41	4a  53	5c  65	6e  77	80  89	92  9b	a4  ad	b6  bf	c8  d1	da  e3	ec  f5	fe
03  0c	15  1e	27  30	39  42	4b  54	5d  66	6f  78	81  8a	93  9c	a5  ae	b7  c0	c9  d2	db  e4	ed  f6	ff
04  0d	16  1f	28  31	3a  43	4c  55	5e  67	70  79	82  8b	94  9d	a6  af	b8  c1	ca  d3	dc  e5	ee  f7
05  0e	17  20	29  32	3b  44	4d  56	5f  68	71  7a	83  8c	95  9e	a7  b0	b9  c2	cb  d4	dd  e6	ef  f8
06  0f	18  21	2a  33	3c  45	4e  57	60  69	72  7b	84  8d	96  9f	a8  b1	ba  c3	cc  d5	de  e7	f0  f9
07  10	19  22	2b  34	3d  46	4f  58	61  6a	73  7c	85  8e	97  a0	a9  b2	bb  c4	cd  d6	df  e8	f1  fa
08  11	1a  23	2c  35	3e  47	50  59	62  6b	74  7d	86  8f	98  a1	aa  b3	bc  c5	ce  d7	e0  e9	f2  fb
256 種類

いけました。

組み立てる

入力データに従って、ファイルを組み立てます。
入力の規格は、以下のodコマンドの出力と同じ、とします。

$ echo あいうえおかきくけこ | od -An -tx1 -v
 e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a e3
 81 8b e3 81 8d e3 81 8f e3 81 91 e3 81 93 0a

組み立てるスクリプトは、最小限ならこれだけで済みます。
・while-read版

# 1バイトの組み立て部品ファイル置き場
byteCellDir="./byteCell0x/"
cd "${byteCellDir}"
while read byteLine ;do
eval cat ${byteLine}
done

1バイトファイルの入ったディレクトリに移動して、「e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a e3」~の文字列を、1行ずつcatするだけ。
1バイトファイルのファイル名を、中身と同じ16進数にしています。
こうすると、入力文字列を加工もせずに、catコマンドにまとめて渡すだけで、数字そのままのバイナリをcatし続けることができる。とても単純。

(evalはzshのために入れてます。bashやdashなら、要りません。)

・xargs版

# 1バイトの組み立て部品ファイル置き場
byteCellDir="./byteCell0x/"
cd "${byteCellDir}"
xargs cat

実際に組むときは、もうちょい追加すると性能が出ます。

#!/bin/sh
#set -x
: << '#--------------------------------------------------------------comment'

■ 1バイトのファイルを組み合わせて、任意のファイルを作成する〜「while read」版

▼ 手順
1)「00〜ff」256種類の1バイトファイルを用意する。
2)これら1バイトファイルから、catコマンドで結合ファイルを作成する。
3)入力データは、16進数を並べたテキストデータとする。


▼ 入力16進数文字列を、そのままcatコマンドの引数に使う
1バイトファイル名を、中身と同名の16進数のみにする。拡張子などは付けない。
入力データを、そのまま引数としてcatコマンドに渡す。
1バイトずつcatを実行するのではなく、1行分、下記では16バイトずつ実行する。

xargsでも同じ処理を書けるはずだが、それはそれ。

▼ odコマンドの出力に限定する
od --endian=big --address-radix=n --format x1 --output-duplicates
 e8 b5 a4 e5 b7 bb e7 b4 99 e9 9d 92 e5 b7 bb e7
 b4 99 e9 bb 84 e5 b7 bb e7 b4 99 0a

・16進数、1バイトずつ、空白区切り。
・改行が必要。16バイトずつ程度だが、増減は許容すべき。

▼速度
$ time cat gulliver-utf8Code02.txt | dash ./binCreator_02.sh >/dev/null
real	0m5.074s
user	0m2.030s
sys	0m2.915s

#--------------------------------------------------------------comment

# 1バイトの組み立て部品ファイル置き場
byteCellDir="./byteCell0x/"

binaryCreator_02(){
: << '#--------------------------------------------------------------comment'
「eval cat」のevalは、zshへの対応。
「tr,sed」の行は、入力の対応の拡大と、高速化。削除しても動作する。
1行を長くすると、高速になる。もちろん限度はある。

行の折り返しはfoldかfmtで行う。
foldは「-s」が必要。終端に改行がつかない。改行のない最終行が失われることを
防ぐため、whileループと直後の「eval cat」をサブシェルの括弧で括る。
#--------------------------------------------------------------comment

cd "${byteCellDir}"

tr -d " " | tr -d "\n" | sed -e 's/../ &/g;y/ABCDEF/abcdef/' |  fold -s -w 1200 |
(
while read byteLine ;do
eval cat ${byteLine}
done
eval cat ${byteLine}
)
}



: << '#------------------------------test'
echo -n "  e8 b5 a4 e5 b7 bb e7 b4 99 e9 9d 92 e5 b7 bb e7
 b4 99 e9 bb 84 e5 b7 bb e7 b4 99 0a " |
#------------------------------test

binaryCreator_02



exit 0

追加で試験しました。WindowsFirefoxの、setup.exe。

$ time 0< ./FirefoxSetup72.0.2.exe od -An -v -tx1 | dash ./binCreator_02.sh >FirefoxSetup72.0.2.exe_copy
real	15m8.692s
user	4m25.774s
sys	10m24.367s
$ md5sum FirefoxSetup72.0.2.exe*
c583a5b233a03141a5308afa8019f76b  FirefoxSetup72.0.2.exe
c583a5b233a03141a5308afa8019f76b  FirefoxSetup72.0.2.exe_copy

いけますね。バイナリファイルでも。

#!/bin/sh
#set -x
: << '#--------------------------------------------------------------comment'

■1バイトのファイルを組み合わせて、任意のファイルを作成する〜「xargs」版
xargsに書き換えて、while-readループを排除する。引数の数の調整なども任せる。
xargsでも、改行のない最終行は、処理できるようだ。

・xargs cat
$ time cat gulliver-utf8Code02.txt | ./binaryCreator_03.sh >/dev/null
real	0m3.001s
user	0m0.931s
sys	0m1.952s

・tr -d "\n" | xargs -d " " -I @@ cat @@
$ time cat gulliver-utf8Code02.txt | ./binaryCreator_03.sh >/dev/null
real	5m59.764s
user	4m18.513s
sys	0m55.739s

#--------------------------------------------------------------comment

# 1バイトの組み立て部品ファイル置き場
byteCellDir="./byteCell0x/"

binaryCreator_03_01(){
(
cd "${byteCellDir}"
xargs cat
)
}

: << '#------------------------------test'
echo -n "  e8 b5 a4 e5 b7 bb e7 b4 99 e9 9d 92 e5 b7 bb e7
 b4 99 e9 bb 84 e5 b7 bb e7 b4 99 0a " |
#------------------------------test

binaryCreator_03_01

exit 0

任意のファイルを作れるのなら、テキストファイルだって作れる

16進数の文字列から、ファイルを作れるようになった。
16進数でなくても、10進数でもいい。ファイル名を変えるだけでいいから。


これはこれで纏めるとして、元の目的に戻ります。cp932のテキストファイルをodコマンドで読み、utf8に変換するには、どうしたらいいか。
結局は先のエントリに載せた、直接に文字をpritfで出力するのが比較的マシだったのだけど、utf8の16進数からテキストファイルに変換するのもやりました。
utf8の16進数にしておけば、別の文字コードにも変えられるだろうし、これはこれでアリだろう。


1バイトファイル方式、気に入ってはいるんすよ。ローテクで。
是非とも、メガネハナーンの切れ端を濃縮したファイルを使いたい。……ああ、これがホメオパシーというものか。なるほど。


つづく。


もうちょい正気なスクリプト

これ、バイナリファイルを、シェルで作るにはどうしたらいいのか、の初歩テストなんだと思います、たぶん。

1バイトファイルの作成スクリプト(そこそこ普通のもの)

#!/bin/sh
#set -x
: << '#--------------------------------------------------------------comment'

■1バイトファイルの自動作成スクリプト
00〜ffの、256種類の1バイトファイルを作成する

_1) xxdコマンド版
_2) 「echo -en "\x??"」版
_3) 「echo -n "\0???"」版
_4) 「printf "\???"」版 

xxdコマンドに変換してもらうと簡単だし、
printfならdashでも「00」を出力できるみたい。
#--------------------------------------------------------------comment

# 共通設定
byteCellGenerator01_comp(){
# 作成するファイルの置き場所
outDir="./byteCellTemp/"
if test -d "${outDir}" ;then true
else exit 1;fi
echo "保存先: ${outDir}" ;ls "${outDir}" ;
echo "実行[y] | やめる [*]"
read action 
if test ${action} = y ;then true
else exit 0 ;fi
}

byteCellGenerator01_1(){ # xxdコマンド版 dash,bash,zsh,ksh

# 16進数 00〜FF アルファベット大文字
hexSeq="00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F \
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F \
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F \
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F \
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F \
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF \
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF \
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF"
# 16進数 00〜ff アルファベット小文字に変換
# ファイル名を大文字にするならコメントアウト
hexSeq=`echo "${hexSeq}" | tr A-F a-f `

eval 'for byte in '${hexSeq}' ; do \
echo -n ${byte} | xxd -r -p > "${outDir}${byte}" ; \
done'

ls "${outDir}"
}


byteCellGenerator01_2(){ # 「echo -en "\x??"」版 使用可:bash,zsh,ksh 不可:dash
# 16進数の00〜ffを作成
hexSeq=` seq 0 255 | xargs -I @@ echo 'obase=16;ibase=10;@@' | bc | sed '1,16s/./0&/' | tr A-F a-f `

echo "${hexSeq}" | 
while read hex ;do
	echo -en "\x${hex}" > ${outDir}${hex}
done

ls "${outDir}"
}


: << '#--------------------------------------------------------------comment'

dash用に8進数指定で作ったが、「00」が作れない。
bashは「echo -en」とする必要がある。

使いみちがない。

#--------------------------------------------------------------comment
byteCellGenerator01_3(){ # 「echo -n "\0???"」版 使用可:zsh,ksh 不可:dash,bash

for n in `seq 0 255`;do
OCT=`echo ${n} | xargs -I @@ echo 'obase=8;ibase=10;@@' | bc `
HEX=`echo "obase=16;ibase=10;${n}" | bc | sed 's/./0&/;s/0\(..\)/\1/;y/ABCDEF/abcdef/' `

eval 'echo -n "\0'${OCT}'" > ${outDir}${HEX}'
# bash用
#eval 'echo -en "\0'${OCT}'" > ${outDir}${HEX}'

done
ls "${outDir}"
}

byteCellGenerator01_4(){ # 「printf "\???"」版 使用可:dash,bash,ksh,zsh 

for n in `seq 0 255`;do

OCT="`printf "%03o" ${n}`"
HEX="`printf "%02x" ${n}`"
eval 'printf "\'${OCT}'" > ${outDir}${HEX}'

done

ls "${outDir}"
}

# 共通
byteCellGenerator01_comp

# どれか一つ
#byteCellGenerator01_1
#byteCellGenerator01_2
#byteCellGenerator01_3
byteCellGenerator01_4


exit 0

バイナリファイル作成(つまんないもの)

zshで動かないのは、forループの変数展開に配慮してないから、だったはず。あんまり気が進まなくて、サボりました。
zsh使うような詳しい人が、こんなん使うはずないし、別に構わんだろう。

#!/bin/dash
#set -x
: << '#--------------------------------------------------------------comment'

■ 任意のファイルを作成する〜変数代入 printf 版

▼ 概要
・printfで『変数に代入』する
printfでバイナリデータを作り、バイナリデータを変数に格納する。
出力は主に「echo -n」で行う。
難しい「00」は、printfで行う。

▼ シェル環境
動作する  :dash,bash,ksh
動作しない:zsh



$ time cat gulliver-utf8Code02.txt | dash ./binaryCreator_05.sh >/dev/null
real	0m2.998s
user	0m1.877s
sys	0m1.095s

$ time cat gulliver-utf8Code02.txt | bash ./binaryCreator_05.sh >/dev/null
real	0m12.197s
user	0m10.855s
sys	0m1.315s

#--------------------------------------------------------------comment



binaryCreator_05_01(){

#HEX00="` printf '\000' `"; 
HEX01="` printf '\001' `"; HEX02="` printf '\002' `"; HEX03="` printf '\003' `"; 
HEX04="` printf '\004' `"; HEX05="` printf '\005' `"; HEX06="` printf '\006' `"; HEX07="` printf '\007' `"; 
HEX08="` printf '\010' `"; HEX09="` printf '\011' `"; HEX0a="` printf '\012' `"; HEX0b="` printf '\013' `"; 
HEX0c="` printf '\014' `"; HEX0d="` printf '\015' `"; HEX0e="` printf '\016' `"; HEX0f="` printf '\017' `"; 
HEX10="` printf '\020' `"; HEX11="` printf '\021' `"; HEX12="` printf '\022' `"; HEX13="` printf '\023' `"; 
HEX14="` printf '\024' `"; HEX15="` printf '\025' `"; HEX16="` printf '\026' `"; HEX17="` printf '\027' `"; 
HEX18="` printf '\030' `"; HEX19="` printf '\031' `"; HEX1a="` printf '\032' `"; HEX1b="` printf '\033' `"; 
HEX1c="` printf '\034' `"; HEX1d="` printf '\035' `"; HEX1e="` printf '\036' `"; HEX1f="` printf '\037' `"; 
HEX20="` printf '\040' `"; HEX21="` printf '\041' `"; HEX22="` printf '\042' `"; HEX23="` printf '\043' `"; 
HEX24="` printf '\044' `"; HEX25="` printf '\045' `"; HEX26="` printf '\046' `"; HEX27="` printf '\047' `"; 
HEX28="` printf '\050' `"; HEX29="` printf '\051' `"; HEX2a="` printf '\052' `"; HEX2b="` printf '\053' `"; 
HEX2c="` printf '\054' `"; HEX2d="` printf '\055' `"; HEX2e="` printf '\056' `"; HEX2f="` printf '\057' `"; 
HEX30="` printf '\060' `"; HEX31="` printf '\061' `"; HEX32="` printf '\062' `"; HEX33="` printf '\063' `"; 
HEX34="` printf '\064' `"; HEX35="` printf '\065' `"; HEX36="` printf '\066' `"; HEX37="` printf '\067' `"; 
HEX38="` printf '\070' `"; HEX39="` printf '\071' `"; HEX3a="` printf '\072' `"; HEX3b="` printf '\073' `"; 
HEX3c="` printf '\074' `"; HEX3d="` printf '\075' `"; HEX3e="` printf '\076' `"; HEX3f="` printf '\077' `"; 
HEX40="` printf '\100' `"; HEX41="` printf '\101' `"; HEX42="` printf '\102' `"; HEX43="` printf '\103' `"; 
HEX44="` printf '\104' `"; HEX45="` printf '\105' `"; HEX46="` printf '\106' `"; HEX47="` printf '\107' `"; 
HEX48="` printf '\110' `"; HEX49="` printf '\111' `"; HEX4a="` printf '\112' `"; HEX4b="` printf '\113' `"; 
HEX4c="` printf '\114' `"; HEX4d="` printf '\115' `"; HEX4e="` printf '\116' `"; HEX4f="` printf '\117' `"; 
HEX50="` printf '\120' `"; HEX51="` printf '\121' `"; HEX52="` printf '\122' `"; HEX53="` printf '\123' `"; 
HEX54="` printf '\124' `"; HEX55="` printf '\125' `"; HEX56="` printf '\126' `"; HEX57="` printf '\127' `"; 
HEX58="` printf '\130' `"; HEX59="` printf '\131' `"; HEX5a="` printf '\132' `"; HEX5b="` printf '\133' `"; 
HEX5c="` printf '\134' `"; HEX5d="` printf '\135' `"; HEX5e="` printf '\136' `"; HEX5f="` printf '\137' `"; 
HEX60="` printf '\140' `"; HEX61="` printf '\141' `"; HEX62="` printf '\142' `"; HEX63="` printf '\143' `"; 
HEX64="` printf '\144' `"; HEX65="` printf '\145' `"; HEX66="` printf '\146' `"; HEX67="` printf '\147' `"; 
HEX68="` printf '\150' `"; HEX69="` printf '\151' `"; HEX6a="` printf '\152' `"; HEX6b="` printf '\153' `"; 
HEX6c="` printf '\154' `"; HEX6d="` printf '\155' `"; HEX6e="` printf '\156' `"; HEX6f="` printf '\157' `"; 
HEX70="` printf '\160' `"; HEX71="` printf '\161' `"; HEX72="` printf '\162' `"; HEX73="` printf '\163' `"; 
HEX74="` printf '\164' `"; HEX75="` printf '\165' `"; HEX76="` printf '\166' `"; HEX77="` printf '\167' `"; 
HEX78="` printf '\170' `"; HEX79="` printf '\171' `"; HEX7a="` printf '\172' `"; HEX7b="` printf '\173' `"; 
HEX7c="` printf '\174' `"; HEX7d="` printf '\175' `"; HEX7e="` printf '\176' `"; HEX7f="` printf '\177' `"; 
HEX80="` printf '\200' `"; HEX81="` printf '\201' `"; HEX82="` printf '\202' `"; HEX83="` printf '\203' `"; 
HEX84="` printf '\204' `"; HEX85="` printf '\205' `"; HEX86="` printf '\206' `"; HEX87="` printf '\207' `"; 
HEX88="` printf '\210' `"; HEX89="` printf '\211' `"; HEX8a="` printf '\212' `"; HEX8b="` printf '\213' `"; 
HEX8c="` printf '\214' `"; HEX8d="` printf '\215' `"; HEX8e="` printf '\216' `"; HEX8f="` printf '\217' `"; 
HEX90="` printf '\220' `"; HEX91="` printf '\221' `"; HEX92="` printf '\222' `"; HEX93="` printf '\223' `"; 
HEX94="` printf '\224' `"; HEX95="` printf '\225' `"; HEX96="` printf '\226' `"; HEX97="` printf '\227' `"; 
HEX98="` printf '\230' `"; HEX99="` printf '\231' `"; HEX9a="` printf '\232' `"; HEX9b="` printf '\233' `"; 
HEX9c="` printf '\234' `"; HEX9d="` printf '\235' `"; HEX9e="` printf '\236' `"; HEX9f="` printf '\237' `"; 
HEXa0="` printf '\240' `"; HEXa1="` printf '\241' `"; HEXa2="` printf '\242' `"; HEXa3="` printf '\243' `"; 
HEXa4="` printf '\244' `"; HEXa5="` printf '\245' `"; HEXa6="` printf '\246' `"; HEXa7="` printf '\247' `"; 
HEXa8="` printf '\250' `"; HEXa9="` printf '\251' `"; HEXaa="` printf '\252' `"; HEXab="` printf '\253' `"; 
HEXac="` printf '\254' `"; HEXad="` printf '\255' `"; HEXae="` printf '\256' `"; HEXaf="` printf '\257' `"; 
HEXb0="` printf '\260' `"; HEXb1="` printf '\261' `"; HEXb2="` printf '\262' `"; HEXb3="` printf '\263' `"; 
HEXb4="` printf '\264' `"; HEXb5="` printf '\265' `"; HEXb6="` printf '\266' `"; HEXb7="` printf '\267' `"; 
HEXb8="` printf '\270' `"; HEXb9="` printf '\271' `"; HEXba="` printf '\272' `"; HEXbb="` printf '\273' `"; 
HEXbc="` printf '\274' `"; HEXbd="` printf '\275' `"; HEXbe="` printf '\276' `"; HEXbf="` printf '\277' `"; 
HEXc0="` printf '\300' `"; HEXc1="` printf '\301' `"; HEXc2="` printf '\302' `"; HEXc3="` printf '\303' `"; 
HEXc4="` printf '\304' `"; HEXc5="` printf '\305' `"; HEXc6="` printf '\306' `"; HEXc7="` printf '\307' `"; 
HEXc8="` printf '\310' `"; HEXc9="` printf '\311' `"; HEXca="` printf '\312' `"; HEXcb="` printf '\313' `"; 
HEXcc="` printf '\314' `"; HEXcd="` printf '\315' `"; HEXce="` printf '\316' `"; HEXcf="` printf '\317' `"; 
HEXd0="` printf '\320' `"; HEXd1="` printf '\321' `"; HEXd2="` printf '\322' `"; HEXd3="` printf '\323' `"; 
HEXd4="` printf '\324' `"; HEXd5="` printf '\325' `"; HEXd6="` printf '\326' `"; HEXd7="` printf '\327' `"; 
HEXd8="` printf '\330' `"; HEXd9="` printf '\331' `"; HEXda="` printf '\332' `"; HEXdb="` printf '\333' `"; 
HEXdc="` printf '\334' `"; HEXdd="` printf '\335' `"; HEXde="` printf '\336' `"; HEXdf="` printf '\337' `"; 
HEXe0="` printf '\340' `"; HEXe1="` printf '\341' `"; HEXe2="` printf '\342' `"; HEXe3="` printf '\343' `"; 
HEXe4="` printf '\344' `"; HEXe5="` printf '\345' `"; HEXe6="` printf '\346' `"; HEXe7="` printf '\347' `"; 
HEXe8="` printf '\350' `"; HEXe9="` printf '\351' `"; HEXea="` printf '\352' `"; HEXeb="` printf '\353' `"; 
HEXec="` printf '\354' `"; HEXed="` printf '\355' `"; HEXee="` printf '\356' `"; HEXef="` printf '\357' `"; 
HEXf0="` printf '\360' `"; HEXf1="` printf '\361' `"; HEXf2="` printf '\362' `"; HEXf3="` printf '\363' `"; 
HEXf4="` printf '\364' `"; HEXf5="` printf '\365' `"; HEXf6="` printf '\366' `"; HEXf7="` printf '\367' `"; 
HEXf8="` printf '\370' `"; HEXf9="` printf '\371' `"; HEXfa="` printf '\372' `"; HEXfb="` printf '\373' `"; 
HEXfc="` printf '\374' `"; HEXfd="` printf '\375' `"; HEXfe="` printf '\376' `"; HEXff="` printf '\377' `"; 
HEX0a='
'


while read byteLine ;do
	for byte in ${byteLine} ;do
	case ${byte} in
	( 00 )
	printf "\000"
	;;
	( * )
	eval 'echo -n "$HEX'${byte}'"'
	;;
	esac
done
done

}


: << '#--------------------test'
echo -n " 5c 2f e7 b3 a0 e5 91 b3 e5 99 8c 2f e3 83 95 e3 82
 a9 e3 83 b3 e3 83 88 2d e3 81 af e6 a5 bd e3 81
 97 e3 81 84 e3 82 88 0a
" |
#--------------------test
binaryCreator_05_01



exit 0
#!/bin/dash
#set -x
: << '#--------------------------------------------------------------comment'

■ 任意のファイルを作成する〜 printf 出力版

▼ 概要
・printf『で出力』する。
変数に格納するのは、8進数3桁、バックスラッシュ付きの「文字列」。
当然、「\000」も格納できる。
入力文字列を変数名に変換して、printfに渡し、printfが8進数を読み取って、
対応したバイナリデータを出力する。

▼ 特徴
・スクリプトファイルひとつで動作可能。
・dashで動作する。
・bashでもけっこう速い。
・kshでは多少遅い。
・つまらない。

▼ シェル環境
動作する  :dash,bash,ksh,zsh


$ time cat gulliver-utf8Code02.txt | bash ./binCreator_06.sh >/dev/null

real	0m3.766s
user	0m1.961s
sys	0m1.836s


#--------------------------------------------------------------comment



binaryCreator_06_01(){

H00="\000"; H01="\001"; H02="\002"; H03="\003"; H04="\004"; H05="\005"; H06="\006"; H07="\007"; 
H08="\010"; H09="\011"; H0a="\012"; H0b="\013"; H0c="\014"; H0d="\015"; H0e="\016"; H0f="\017"; 
H10="\020"; H11="\021"; H12="\022"; H13="\023"; H14="\024"; H15="\025"; H16="\026"; H17="\027"; 
H18="\030"; H19="\031"; H1a="\032"; H1b="\033"; H1c="\034"; H1d="\035"; H1e="\036"; H1f="\037"; 
H20="\040"; H21="\041"; H22="\042"; H23="\043"; H24="\044"; H25="\045"; H26="\046"; H27="\047"; 
H28="\050"; H29="\051"; H2a="\052"; H2b="\053"; H2c="\054"; H2d="\055"; H2e="\056"; H2f="\057"; 
H30="\060"; H31="\061"; H32="\062"; H33="\063"; H34="\064"; H35="\065"; H36="\066"; H37="\067"; 
H38="\070"; H39="\071"; H3a="\072"; H3b="\073"; H3c="\074"; H3d="\075"; H3e="\076"; H3f="\077"; 
H40="\100"; H41="\101"; H42="\102"; H43="\103"; H44="\104"; H45="\105"; H46="\106"; H47="\107"; 
H48="\110"; H49="\111"; H4a="\112"; H4b="\113"; H4c="\114"; H4d="\115"; H4e="\116"; H4f="\117"; 
H50="\120"; H51="\121"; H52="\122"; H53="\123"; H54="\124"; H55="\125"; H56="\126"; H57="\127"; 
H58="\130"; H59="\131"; H5a="\132"; H5b="\133"; H5c="\134"; H5d="\135"; H5e="\136"; H5f="\137"; 
H60="\140"; H61="\141"; H62="\142"; H63="\143"; H64="\144"; H65="\145"; H66="\146"; H67="\147"; 
H68="\150"; H69="\151"; H6a="\152"; H6b="\153"; H6c="\154"; H6d="\155"; H6e="\156"; H6f="\157"; 
H70="\160"; H71="\161"; H72="\162"; H73="\163"; H74="\164"; H75="\165"; H76="\166"; H77="\167"; 
H78="\170"; H79="\171"; H7a="\172"; H7b="\173"; H7c="\174"; H7d="\175"; H7e="\176"; H7f="\177"; 
H80="\200"; H81="\201"; H82="\202"; H83="\203"; H84="\204"; H85="\205"; H86="\206"; H87="\207"; 
H88="\210"; H89="\211"; H8a="\212"; H8b="\213"; H8c="\214"; H8d="\215"; H8e="\216"; H8f="\217"; 
H90="\220"; H91="\221"; H92="\222"; H93="\223"; H94="\224"; H95="\225"; H96="\226"; H97="\227"; 
H98="\230"; H99="\231"; H9a="\232"; H9b="\233"; H9c="\234"; H9d="\235"; H9e="\236"; H9f="\237"; 
Ha0="\240"; Ha1="\241"; Ha2="\242"; Ha3="\243"; Ha4="\244"; Ha5="\245"; Ha6="\246"; Ha7="\247"; 
Ha8="\250"; Ha9="\251"; Haa="\252"; Hab="\253"; Hac="\254"; Had="\255"; Hae="\256"; Haf="\257"; 
Hb0="\260"; Hb1="\261"; Hb2="\262"; Hb3="\263"; Hb4="\264"; Hb5="\265"; Hb6="\266"; Hb7="\267"; 
Hb8="\270"; Hb9="\271"; Hba="\272"; Hbb="\273"; Hbc="\274"; Hbd="\275"; Hbe="\276"; Hbf="\277"; 
Hc0="\300"; Hc1="\301"; Hc2="\302"; Hc3="\303"; Hc4="\304"; Hc5="\305"; Hc6="\306"; Hc7="\307"; 
Hc8="\310"; Hc9="\311"; Hca="\312"; Hcb="\313"; Hcc="\314"; Hcd="\315"; Hce="\316"; Hcf="\317"; 
Hd0="\320"; Hd1="\321"; Hd2="\322"; Hd3="\323"; Hd4="\324"; Hd5="\325"; Hd6="\326"; Hd7="\327"; 
Hd8="\330"; Hd9="\331"; Hda="\332"; Hdb="\333"; Hdc="\334"; Hdd="\335"; Hde="\336"; Hdf="\337"; 
He0="\340"; He1="\341"; He2="\342"; He3="\343"; He4="\344"; He5="\345"; He6="\346"; He7="\347"; 
He8="\350"; He9="\351"; Hea="\352"; Heb="\353"; Hec="\354"; Hed="\355"; Hee="\356"; Hef="\357"; 
Hf0="\360"; Hf1="\361"; Hf2="\362"; Hf3="\363"; Hf4="\364"; Hf5="\365"; Hf6="\366"; Hf7="\367"; 
Hf8="\370"; Hf9="\371"; Hfa="\372"; Hfb="\373"; Hfc="\374"; Hfd="\375"; Hfe="\376"; Hff="\377"; 


tr -d " " | tr ABCDEF abcdef | tr -d "\n" | ( fold -w128 ; echo ) | sed 's/../${H&}/g' |
while read byteLine ;do
	eval 'printf "'${byteLine}'"'
done

}


: << '#--------------------test'
echo -n " 5c 2f e7 b3 a0 e5 91 b3 e5 99 8c 2f e3 83 95 e3 82
 a9 e3 83 b3 e3 83 88 2d e3 81 af e6 a5 bd e3 81
 97 e3 81 84 e3 82 88 0a
" |
#--------------------test
binaryCreator_06_01



exit 0

・追加試験

$ time 0< FirefoxSetup72.0.2.exe od -An -tx1 -v | ./binCreator_06.sh >FirefoxSetup72.0.2.exe_copy
real	5m52.308s
user	2m22.637s
sys	3m53.581s
$ md5sum FirefoxSetup72.0.2.exe*
c583a5b233a03141a5308afa8019f76b  FirefoxSetup72.0.2.exe
c583a5b233a03141a5308afa8019f76b  FirefoxSetup72.0.2.exe_copy

printfだと1バイトファイルなんて要らんし、3倍近く早いみたいだけど。面白くはないなぁ。
イヤなんすよ。「そんなことしなくても〇〇ならできますよ」は。