2018/04/30

シェルスクリプトに画像データを埋め込んで画像ファイルを出力する

シェルスクリプトの中に埋め込んだ小さなアイコンなどの画像を実行時に書き出すための方法を扱い、記事の最後では、文字列を描画した画像をコマンドで作る方法も扱う。

  1. 自分でスクリプトを記述する方法
    1. XPMデータを用いる場合
    2. base64を用いる場合
    3. convertコマンド使用時のパイプの使用と形式の指定
  2. sharを用いる方法
  3. 関連:文字列を描画した画像をコマンドで出力する

自分でスクリプトを記述する方法

XPMデータを用いる場合

XPMという画像ファイル形式がテキスト形式であることを利用して、ImageMagickの画像形式変換コマンドconvertと組み合わせる。

  1. あらかじめ画像をXPM形式で保存しておき、(これをコピペするなどして)ヒアドキュメントによってそのXPMファイルの内容を書き出すコードを記述
  2. ImageMagickのconvertコマンドでそれを変換

この方法には

  • $のような特殊な文字が含まれる場合にバックスラッシュ付きにする必要がある
  • スクリプト内の画像データの量が多くなる

といった問題点があり、画像の色数が非常に少なくサイズも小さい場合以外はあまりおすすめできない。

base64を用いる場合

画像をbase64でテキストデータ化する方法で、任意の画像形式が利用できる。base64エンコードされたデータは元の画像データよりサイズが増えるが、PNGなどにすることで、XPMデータをそのまま記述するよりは小さくできる。

  1. 任意の画像形式で保存したファイルをbase64であらかじめエンコードし、(これをコピペするなどして)ヒアドキュメントによってその画像ファイルの内容を書き出すコードを記述
  2. 同コマンドでデコードを行い、ファイルを出力
  3. 必要があればImageMagickのconvertコマンドでそれを変換

convertコマンド使用時のパイプの使用と形式の指定

convertコマンドを用いる場合、パイプによって(中間ファイルなしで)スクリプト内のデータから直接変換後のファイルを出力することができる。その際の引数の指定は

  • 最初の引数(入力ファイル)にxpm:-
  • 2つめの引数(出力ファイル)にpng:-など(必要に応じて出力先の形式に合わせる)

となる。

下のスクリプトを実行すると、中に記述された画像データから

  • icon.png: icon.png
  • human.png: human.png

の2つの画像ファイルを出力する。前者はXPM画像のデータをconvertでPNG形式に変換し、後者はPNG形式で作成したファイルをbase64でエンコードしたものをデコードする。

[任意]ファイル名:gen-icons-test.sh
#! /bin/sh

cat <<_EOF_ | convert xpm:- png:- > icon.png
/* XPM */
static char * icon_xpm[] = {
"48 48 8 1",
"  c #FFB300",
". c #00A1FF",
"+ c #FFFFFF",
"@ c #B7E6DD",
"# c #0E5D11",
"\$    c #975D11",
"% c #FDE378",
"& c #132F7E",
"         ..........................++++++++++...",
"         .........................+++++++++.....",
"        ...........................+++..........",
"        ........................................",
"       ....................+++++++++++++++++++++",
"       ...............................++++++++++",
"      +++..............................+++++++++",
"     ++++++++..................................+",
"  ++++++++++++...+++............................",
"+++++++++++++...++++++++++......................",
".................+++++.........+++..............",
"..............................+++++++++++.......",
"...........................+++++++++++++++++++++",
"......................++++++++++++++++++++++++++",
"++++++++++++++...........................+++++++",
"................................................",
"............+++++...............................",
"........++++++++++..............................",
".++++++++++++++++...............................",
"+++++++++++++...................................",
"+++++++++.......................................",
"+..............................................@",
"................................................",
"................................................",
"................................................",
"................................................",
"................................................",
"................................................",
"................................................",
"................................................",
"................................................",
"######..........................................",
"#########.......................................",
"###########.....................................",
"\$\$\$\$\$\$######\$\$\$\$%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&",
"\$\$\$\$\$\$\$\$\$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&",
"%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&"};
_EOF_

cat <<_EOF_ | base64 -d > human.png
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACslBMVEX+/v7+2/X+8vv+3vb+
8Pr+u+z+iuD+gd3+u+3+4fb+8fv+9vz+/f7+9Pv+6/n+s+r+gt7+P8z+LMf+K8f+YtX+ren+4vf+
8/v+/f3+7/r+9fz+/P3+1/T+oeb+M8n+HcT+IcX+S9D+SM/+L8j+GML+UtH+vO3+6Pj+3/b+adf+
Kcf+C7/+FMH+A73+Abz+ALz+EsH+ctr+zfH+2vT+3PX+TdD+Db/+D8D+Nsr+hN7+tev+ve3+Bb3+
Zdb+1PP+UNH+G8P+h9/+0vL++/3+0/P+0fL+hd/+FcH+Lsj+neX++Pz+3fX+wu7+I8X+kuL+yPD+
we7+X9X+Cr/+Pcz+e9z+FsL+hd7+zvH+wO7+Ar3+IMT+2PT++v3+Ksf+Qs3+uez+Rs7+Cb7+yvD+
xe/+J8b+juH+7vr+vu3+lOL+k+L+1fP+YdX+W9T+Psz+DcD+LMj+XNT+v+3+leP+HMP+DL/+WtP+
d9v+O8v+atf+oOb+oOX+Sc/+aNf+B77+Ocv+S8/+JMX+H8T+Ss/+PMz+F8L+IsX+luP+2vX+Qc3+
8fr+w+/+4/f+1vP+6fj+0PL+7Pn+bNj+n+X+Arz+9/z+0vP+Mcn+z/L+UdH+Ncr+VNL+Br7+tOv+
GsP+NMr+Msn+v+7+Ztf+3vX+N8r+YNX+qej+w+7+ddr+oub+btn+Y9b+E8H+CL7+FcL+BL3+2fT+
DsD+Rc7+OMv+Jcb+6vn+9fv+Lcj+keL+EMD+Ztb+HsT+a9j+pef+sOr+r+n+Jsb+yfD+x+/+KMb+
Bb7+zPH+Cb/+g97+uOz+7fn+XtT+kOH++fz+XtX+nuX+KMf++f3+meT+RM7+XdT+EcH+1vT+uuz+
c9r+GcP+U9L+5/j+dNr+jOD+5Pf+NMn+T9H+4Pb+TND+ser+dtv+rOn+gN3+edz///8ty8h3AAAA
AWJLR0TlWGULvwAAAAd0SU1FB+IEEwwkBN/rkkMAAAWOSURBVBgZfcGLYxN3AcDxb+5wGu4XFMvy
S7gM4lBomia/mnZtYd0xliu0XKEHpDYll7UbtQQwc2LHHIJbN7TQrtSthpfKZA/ZwIwpQ2Rz6Hzs
PXXzNd/PP8QU2olC+/kwyYfGf/n0OR/gOj74If9c0DCYJkDnMoNpAWDehz8y/6NV8xdcH5SECBuC
90lgoWYyRRggidywaHH0YzcuiX78E0uXAQFD8D4BgmmCsITqWE28NpFUKlFXV/PJIKn6ELNpuKlR
NTUvX3HziuXNTS2LbwliSWZRvbIpeeuq2ywtLe3W1Wvibe1pNGFyLeZapyPdXtW0bn2nS4WTcjds
VJuQSGbgQEZ1LflUdwMVQuvO9qjN5DyuTaDn0u35utt7fRYGZsBv20o1CnRmIi1yfY13rLxzixEO
CeHfcptSjcLWmIW3tF9FP70sgjkQ9rE1qaIi5wSYhSzUqDXbtms4enfnjsRniqEOycw0UlbsLvXZ
uz9n21bnzs8P5tvTFrMJaQTvie+K3/uF+3bvuDUZ/6LO7KQJkT35vU2JZE8iPvilHAuRzE4Esvdv
fKAqMziUfzCHw4xCmCYV9X7L7Xlo3+CXN32lGoaZYlDhMU0IKgSQDmvSUZn4ftfRQA4YgksEIAEB
aOA4SAQSFgrNOpDMJNzUiHAIcZmQPg1I1QOjadLYNh246LqQMthxIJlRzsOjCNJcZjLG2MExJqVI
AwIBa23GSYXRVSY5x9YwmaJhpKyvZh5JRxjG09uLm+OP9gxGi7GdrjPxNasjq0rqgA0CTBNkmG53
bFtpQYYsTBw6XFV1ZGVGHT6668ixaHFP4b4b9rcklDMSFFIKIajolta2xU1t7emvIw/d+I3aRI9K
KqWOfbNPqeZjqmdw3/GerO44elDzoyPHc4/d3Z/sumUU6NxdeqDqW1ln7twTbmFPMXrscfVEqVhV
TGx4MmVbFk/ZKWtO9tv3blQ1sVE4SOfJp2/qtSzbtUaw1+ruzmeK0cSpuDr9nQ3uCWmampMt3DO/
fHqwv2CRaogQePZgTko5oJlBwMaeo2f3n3lu0XcHE0olVUWyvKhr8/f6lgZSDA/kYAK6zz5vCAwN
HGTAf/3JHlUbz1cdVUcrlDpy7sym7+c8Jp1FB42BkABMMKRoCFu7lTrf/IMLXfGepHo0vnnTCy9m
PfQJDGHwf4QwpW4vT5wuPZ1tmJsWFYD3rHQhYICANP8r7G9obR6MLzlp/3CLnU7bT1k58DMjH1n/
6n2ldfmXfHKUS6R0NAyTqwkIdWOvX1POn9p78UfGj8EADSlAcDWRDtFNdezlJ3+SaIyrmp96iDAz
EoBwvNa+n6mhHT+/rrlr1cVXXjW8ejTJNZmgu7L3XEJdXGFlrTtXr3ltaJVlpaQNGpNMrrK9MKRU
PqXpTjjiX3+8vO/x1903Hh7HZpIIcYUBzfFysRqVKFnoPmn4hPVC6Ym7ysU333LfiHAFQcXzIZBL
b994/o6+HCkNwwDGthXP1Kp8S/HNt9wJK4LFJQaTJFahv9zydqwaY9g0gOEUv3B/+avzLz+YON5S
fKfgnkiPYxEG1uJzqI693fLuQ+2/Bl8YAQZYjEQGfhMr7i0ldy0qR4u/fSwcwMEnBXitffF4f2xE
mtRjUCGoCNrmPM99JFaMlo/vUlW/+/3NywQDWmCh7G0rX3julfe8UdAMpghkkFGIeCeyhdeK5QsL
6vJ/8DPqeVZhSA390dII/SnMlURAjgJZcrbhFuY3vVv359cZk4eeKZ1r6523xRomZDBFIEwqBGga
9dKal3vvpb8sWZfhr1v/Vnuqr9XbTljjGgQCkGEH9+yrf39nKy8eVm3/0NkOCEGFIIDEA50raehO
drzb5Z//+nfnAN0hEFxihDUbbPBggGkhkJqD5/4HBY1k9c90F34AAAAldEVYdGRhdGU6Y3JlYXRl
ADIwMTgtMDQtMTlUMTI6MzY6MDQrMDk6MDCQZ5hSAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA0
LTE5VDEyOjM2OjA0KzA5OjAw4Tog7gAAAABJRU5ErkJggg==
_EOF_

sharを用いる方法

シェルスクリプトからの画像ファイルの出力にはsharを用いる方法もある。このコマンドが出力するshar書庫は自己展開書庫の一種(外部プログラム不要で展開可能)で、シェルスクリプトファイルのダイジェスト(ハッシュ)が展開時に計算されて中身が正しいかを確認でき、XZ圧縮もサポートされている(要オプション指定)。また、最終更新日時やパーミッションも保存され、展開時に復元できる。

(icon.pngとhuman.pngをicons.sharに圧縮する例)
$ shar -C xz icon.png human.png > icons.shar

中身はこのようになった。

[任意]ファイル名:icons.shar
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh01234
# Made on YYYY-MM-DD hh:mm JST by <user@host>.
# Source directory was '/path/to/srcdir'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    450 -rw-r--r-- icon.png
#   2371 -rw-r--r-- human.png
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
  echo 'Note: not verifying md5sums.  Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
  if test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    case `$dir/gettext --version 2>&1 | sed 1q` in
      *GNU*) gettext_dir=$dir
      set_echo=true
      break ;;
    esac
  fi
done

if ${set_echo}
then
  set_echo=false
  for dir in $PATH
  do
    if test -f $dir/shar \
       && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
    then
      locale_dir=`$dir/shar --print-text-domain-dir`
      set_echo=true
      break
    fi
  done

  if ${set_echo}
  then
    TEXTDOMAINDIR=$locale_dir
    export TEXTDOMAINDIR
    TEXTDOMAIN=sharutils
    export TEXTDOMAIN
    echo="$gettext_dir/gettext -s"
  fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
     then shar_n= shar_c='
'
     else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if   touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
     test ! -f ${st1} && test -f ${f}; then
  shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
     test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
  shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
     test ! -f ${st3} && test -f ${f}; then
  shar_touch='touch -am $3$4$5$6$2 "$8"'

else
  shar_touch=:
  echo
  ${echo} 'WARNING: not restoring timestamps.  Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
  echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
     exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
     exit 1
fi
# ============= icon.png ==============
if test -n "${keep_file}" && test -f 'icon.png'
then
${echo} "x - SKIPPING icon.png (file already exists)"

else
${echo} "x - extracting icon.png xz-compressed"
  sed 's/^X//' << 'SHAR_EOF' | uudecode &&
begin 600 _sh01234/xzi
M_3=Z6%H```3FUK1&`@`A`1P````0SUC,X`'!`91=`$24!<1Z)_;W[HF.4)"(
MLZK54"O\DKAHR$*/!5#*TIW1L`NS\(&ER%C?T'3K@:;ZK-Y3$0V\;U<./7]3
M._5-ETQ;![>1B;3U4U!PL"2?E2`58M!#\Y0P/X]J65CCV$NEM]V-`I$3F4-K
MW-QA'46-&!3@7%_K;9^9V6J6SX]Y:?/-T;-:#XITZCK46)*T@_L7(8NPVM>5
MP.^XW9SF0,B'BI&(D/8/HX02]WD$J+H?4^'XNE7`UEO8V^(/S<7M^F3;Q>4-
M9DPBWP'4,^1Y#RD/:YPIXG\[%C_8KG59.;B$%L^Z*\M"&D]XSTR,+W@N.1[$
M'&V`JS'?6D+6C<UWC8;TIL;Y+]?1EO[2Y,M+1<W_\[:H/_\5K[3QI,RQ]+7Y
M;M1DFH2O>K:N_*>AK='&JOI.^.K8%2?U7P5I&MS_"602`MSN["9-4B4*R^\*
MW8]PUL3(H[3S5B'+'?&IVM%AIG\6!K@=IB,[C%?7D?46D.-K]:YJ[`H<OA*,
MAK.YF">2<IQSED*#52`M)E%QV.L.2G@(&C=O=N&@`)XCV"YD4K(D``&P`\(#
2``"L)3U`L<1G^P(`````!%E:
`
end
SHAR_EOF
${echo} xz-decompressing file icon.png &&

xz -dc ${lock_dir}/xzi > 'icon.png' && \
  (set 20 18 04 01 00 00 00 'icon.png'
   eval "${shar_touch}") && \
  chmod 0644 'icon.png'
if test $? -ne 0
then ${echo} "restore of icon.png failed"
fi
  if ${md5check}
  then (
       ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'icon.png': 'MD5 check failed'
       ) << \SHAR_EOF
fb4f6fbe26cb3e7168e9487df79a3024  icon.png
SHAR_EOF

else
test `LC_ALL=C wc -c < 'icon.png'` -ne 450 && \
  ${echo} "restoration warning:  size of 'icon.png' is not 450"
  fi
fi
# ============= human.png ==============
if test -n "${keep_file}" && test -f 'human.png'
then
${echo} "x - SKIPPING human.png (file already exists)"

else
${echo} "x - extracting human.png xz-compressed"
  sed 's/^X//' << 'SHAR_EOF' | uudecode &&
begin 600 _sh01234/xzi
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
+Q&?[`@`````$65KU
`
end
SHAR_EOF
${echo} xz-decompressing file human.png &&

xz -dc ${lock_dir}/xzi > 'human.png' && \
  (set 20 18 04 01 00 00 00 'human.png'
   eval "${shar_touch}") && \
  chmod 0644 'human.png'
if test $? -ne 0
then ${echo} "restore of human.png failed"
fi
  if ${md5check}
  then (
       ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'human.png': 'MD5 check failed'
       ) << \SHAR_EOF
950952f50d010626ac2a96017bb9322c  human.png
SHAR_EOF

else
test `LC_ALL=C wc -c < 'human.png'` -ne 2371 && \
  ${echo} "restoration warning:  size of 'human.png' is not 2371"
  fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
     exit 1
fi
exit 0

シェルスクリプトの一部としてshar書庫の展開処理を記述する場合は本体のスクリプト内の展開したいところで

sh /path/to/icons.shar

などのように記述して書庫を実行する[1]か、unsharで展開する。

関連:文字列を描画した画像をコマンドで出力する

convertコマンドには文字列の描画機能があり、入力ファイル部分をlabel:[文字列]とした上でオプションでフォントや色などを指定することで、文字列の描画された画像を簡単に生成することができる。テキストの内容にコマンド実行結果や変数値を入れることで、動的な画像生成処理と言えるものとなる。

下は実行例。実際には色,フォントの場所とサイズ,ラベルの部分を変えて実行し、必要があれば他の指定も追加する。

(画面上で確認)
$ convert -background #4466aa -fill #ffffff -font /usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc -pointsize 72 label:")))飛脚" png:- | display png:-

(PNGファイルを出力)
$ convert -background #ffff00 -fill #000000 -font /usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc -pointsize 48 label:"とらん\nスポート\n黒い猫\n$(date "+%Y/%m/%d %H:%M:%S") 生成" text.png

ImageMagick公式サイトにある使用例も参照。

使用したバージョン:
  • sharutils 4.15.2
  • ImageMagick 6.9.7-4 Q16 20170114
[1]: 実行属性が付いている場合はパス名指定でも可