コマンドライン形式の文字列を受け取って解析し、要素の集まりを得る処理を行いたい場面が存在するが、手動で行うのは面倒で、正確でない場合もありうる。
ここでは、Pythonの標準機能とGLibライブラリのそれぞれにおいてこの処理を行ってくれる関数についてを扱う。
元の記事は2010年1月に書かれたが、大幅に内容を書き直しており、GLibについての解説と例を追加している。
Python
文字列オブジェクトのメンバ関数split()
はスペースやクォートを含んだコマンドライン文字列を意図した通り(クォート内をひとまとめにしてその中のスペースでは区切らない)に区切ってくれない。
そこで、shlex.split()
関数を用いて文字列を渡すことでスペースやクォートがうまく処理され、リスト型の戻り値として要素の集まりを得ることができる。以前試したときにある方にコメントで紹介していただいた方法。
parse-cmdline.py
#! /usr/bin/python
from __future__ import print_function
import shlex
str1 = 'aaa bbb ccc "ddd eee fff" ggg "hhh iii "jjj'
str2 = 'aaa bbb ccc "ddd eee"fff ggg "h"hh iii'
str3 = ' ls -l -a "a b c.txt"'
str4 = ' ls -l -a c"d e f.tx"t'
print ('"{0}" -> {1}'.format (str1, shlex.split (str1)))
print ('"{0}" -> {1}'.format (str2, shlex.split (str2)))
print ('"{0}" -> {1}'.format (str3, shlex.split (str3)))
print ('"{0}" -> {1}'.format (str4, shlex.split (str4)))
実行結果"aaa bbb ccc "ddd eee fff" ggg "hhh iii "jjj" -> ['aaa', 'bbb', 'ccc', 'ddd eee fff', 'ggg', 'hhh iii jjj'] "aaa bbb ccc "ddd eee"fff ggg "h"hh iii" -> ['aaa', 'bbb', 'ccc', 'ddd eeefff', 'ggg', 'hhh', 'iii'] " ls -l -a "a b c.txt"" -> ['ls', '-l', '-a', 'a b c.txt'] " ls -l -a c"d e f.tx"t" -> ['ls', '-l', '-a', 'cd e f.txt']
GLib
C言語やVala言語で使用可能なライブラリGLibのg_shell_parse_argv()
(Vala言語ではGLib.Shell.parse_argv()
)も同様の処理を行う。
GLibはLGPLライセンスだが、MonoプロジェクトによるMITライセンスの実装eglibでもこの関数は利用できる。
C言語での使用例
文字列以外にgint
型,gchar **
型,GError *
型の変数を用意してこの関数を呼び出し、引数の数と内容(構文解析エラーになった場合はその情報を含んだGError
)を得る。
正常に処理が行われた場合、必要のなくなった内容部分(gchar **
型)はg_strfreev()
で解放する必要がある。
glib-parse-cmdline.c
/*
* GLib (LGPL 2.1+):
* gcc -O2 -Wall -Wextra $(pkg-config --cflags glib-2.0) glib-parse-cmdline.c -o glib-parse-cmdline $(pkg-config --libs glib-2.0)
*
* eglib (MIT):
* gcc -O2 -Wall -Wextra -Ieglib -Ieglib/src glib-parse-cmdline.c eglib/src/.libs/libeglib.a -o glib-parse-cmdline-eglib
*/
#include <stdlib.h>
#include <glib.h>
gboolean
print_cmdline (const gchar *str)
{
gint i;
gint argc;
gchar **argv;
GError *error = NULL;
g_shell_parse_argv (str, &argc, &argv, &error);
if (error)
{
g_printerr ("g_shell_parse_argv () failed (%s): %s\n", str, error->message);
g_error_free (error);
return FALSE;
}
g_print ("\"%s\" -> ", str);
for (i = 0; i < argc; i++)
{
g_print (i ? ", " : "[");
g_print ("\"%s\"", argv[i]);
}
g_print ("]\n");
g_strfreev (argv);
return TRUE;
}
int
main ()
{
const gchar *str1 = "aaa bbb ccc \"ddd eee fff\" ggg \"hhh iii \"jjj";
const gchar *str2 = "aaa bbb ccc \"ddd eee\"fff ggg \"h\"hh iii";
const gchar *str3 = " ls -l -a \"a b c.txt\"";
const gchar *str4 = " ls -l -a c\"d e f.tx\"t";
if (! print_cmdline (str1))
return EXIT_FAILURE;
if (! print_cmdline (str2))
return EXIT_FAILURE;
if (! print_cmdline (str3))
return EXIT_FAILURE;
if (! print_cmdline (str4))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
実行結果"aaa bbb ccc "ddd eee fff" ggg "hhh iii "jjj" -> ["aaa", "bbb", "ccc", "ddd eee fff", "ggg", "hhh iii jjj"] "aaa bbb ccc "ddd eee"fff ggg "h"hh iii" -> ["aaa", "bbb", "ccc", "ddd eeefff", "ggg", "hhh", "iii"] " ls -l -a "a b c.txt"" -> ["ls", "-l", "-a", "a b c.txt"] " ls -l -a c"d e f.tx"t" -> ["ls", "-l", "-a", "cd e f.txt"]
Vala言語での使用例
GLib.Shell.parse_argv()
は2番目の引数に文字列配列(未初期化でも可)をout
指定で渡すことでこの配列に取得済みの内容が入る。これの解放処理は文字列配列の変数が寿命となったときに自動的に行われる。
parse-cmdline.vala
/*
* valac --pkg posix parse-cmdline.vala -o parse-cmdline
*/
using Posix;
bool
print_cmdline (string str)
{
try
{
string[] argv;
GLib.Shell.parse_argv (str, out argv);
print ("\"%s\" -> ", str);
for (int i = 0; i < argv.length; i++)
{
print (i != 0 ? ", " : "[");
print ("\"%s\"", argv[i]);
}
print ("]\n");
}
catch (GLib.ShellError e)
{
printerr ("GLib.Shell.parse_argv() failed (%s): %s\n", str, e.message);
return false;
}
return true;
}
int
main (string[] args)
{
const string str1 = "aaa bbb ccc \"ddd eee fff\" ggg \"hhh iii \"jjj";
const string str2 = "aaa bbb ccc \"ddd eee\"fff ggg \"h\"hh iii";
const string str3 = " ls -l -a \"a b c.txt\"";
const string str4 = " ls -l -a c\"d e f.tx\"t";
if (! print_cmdline (str1))
return EXIT_FAILURE;
if (! print_cmdline (str2))
return EXIT_FAILURE;
if (! print_cmdline (str3))
return EXIT_FAILURE;
if (! print_cmdline (str4))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
実行結果は上のC言語版と同じなので省略する。
- Python 2.7.14, 3.6.3
- GLib 2.54.1
- GCC 7.2.0
- Vala 0.36.6