LoginSignup
2
1

More than 5 years have passed since last update.

シェルスクリプトでPythonのArgumentParserを使う

Last updated at Posted at 2018-08-28

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です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1