Pythonに慣れると、シェルスクリプトのgetoptがあまりに非力で泣きたくなってくるので、この部分だけArgumentParser
のパワーを拝借する方法を考えてみました。
# !/bin/bash
args_source="$(mktemp -u)"
PROGNAME="$0" ARGS_SOURCE="${args_source}" python3 - "$@" <<'__EOF__' || exit $?
from argparse import ArgumentParser
from os import environ
p = ArgumentParser(prog=environ['PROGNAME'])
# コマンドラインのオプションを指定する。
p.add_argument('--arg1', '-a', help='argument 1')
p.add_argument('--switch2', '-s', action='store_true', help='switch 2')
p.add_argument('arg3', nargs='+', help='position-fixed arguments 3')
def py2bash(v):
if type(v) is list:
return '('+' '.join(map(py2bash, v))+')'
elif type(v) is bool:
return f'/bin/{repr(v).lower()}'
else:
return "$'"+''.join(f'\\{c:03o}' for c in str(v).encode())+"'"
a = p.parse_args()
with open(environ['ARGS_SOURCE'], 'x') as fp:
for k, v in vars(a).items():
if v is None: continue
print(f'{k}={py2bash(v)}', file=fp)
__EOF__
test -e "${args_source}" || exit 0 # printing --help
source "${args_source}"
rm "${args_source}"
# 以降、Bashの処理
- リストで受け取るパラメータ(
nargs
が指定されているもの)は、Bashでもリスト変数として受け取れます。 - Bool値として受け取れるパラメータは、
'/bin/true'
または'/bin/false'
という文字列でBash変数に渡されます。これでBashのif
等の評価にそのまま渡せます。 - それ以外は
str()
で文字列に変換したうえで、Bash変数に渡します。8進表記でエスケープしているので、記号や日本語が混じっても大丈夫なはず。 - 省略されたパラメータはシェル変数が定義されないので、
test "${omittable:+x}" = 'x' && echo 'specified'
等で確認が可能。
Python2.7でもやってみた
# !/bin/bash
args_source="$(mktemp -u)"
PROGNAME="$0" ARGS_SOURCE="${args_source}" python2.7 - "$@" <<'__EOF__' || exit $?
# -*- coding: utf-8 -*-
from __future__ import print_function
from argparse import ArgumentParser
from os import environ
from os.path import exists
p = ArgumentParser(prog=environ['PROGNAME'])
# コマンドラインのオプションを指定する。
p.add_argument('--arg1', '-a', help='argument 1')
p.add_argument('--switch2', '-s', action='store_true', help='switch 2')
p.add_argument('arg3', nargs='+', help='position-fixed arguments 3')
def py2bash(v):
if type(v) is list:
return '(%s)' % ' '.join(map(py2bash, v))
elif type(v) is bool:
return '/bin/%s' % repr(v).lower()
else:
return "$'%s'" % ''.join('\\%03o' % ord(c) for c in str(v))
a = p.parse_args()
args_source = environ['ARGS_SOURCE']
if exists(args_source):
raise OSError("File exists: %s" % repr(args_source))
with open(args_source, 'w') as fp:
for k, v in vars(a).items():
if v is None: continue
print('%s=%s' % (k, py2bash(v)), file=fp)
__EOF__
test -e "${args_source}" || exit 0 # printing --help
source "${args_source}"
rm "${args_source}"
# 以降、Bashの処理
使えない機能がいくつかあったので多少煩雑になりましたが、まだPython3.xよりも普及していると思われるPython2.7でもOKです。