端末でプログラムを実行するときはいつでも、プログラムが期待するいくつかの引数を渡すことができます。これらの引数は、プログラムの実行中に使用できます。ここで、システムは、プログラムの実行中にユーザーから渡されたすべての引数を維持するための内部機能を提供します。これらの引数は「コマンド ライン引数」として知られています。
このチュートリアルでは、コマンド ライン引数の理解を実際のプログラムにマッピングして、より明確に理解できるようにします。ただし、プログラムにジャンプする前に、システムがコマンドライン引数の機能をどのように提供するかを知っておく必要があります。ご存知のように、すべての C プログラムには main() 関数が必要であり、コマンド ライン引数の機能は main() 関数自体によって提供されます。以下の宣言がプログラムで使用されている場合、プログラムにはコマンドライン引数を使用/操作する機能があります。
int main (int argc, char *argv[])
ここで、argc パラメーターは、実行時に実行可能ファイルに渡されるコマンド ライン引数の総数です (最初の引数として実行可能ファイルの名前を含む)。 argv パラメータは、実行時に実行可能ファイルに渡される各コマンド ライン引数の文字列の配列です。 C プログラミングが初めての場合は、まず C 配列がどのように機能するかを理解する必要があります。
以下は、コマンドライン引数を使用した作業プログラムです。
#include <stdio.h> int main (int argc, char *argv[]) { int i=0; printf("\ncmdline args count=%s", argc); /* First argument is executable name only */ printf("\nexe name=%s", argv[0]); for (i=1; i< argc; i++) { printf("\narg%d=%s", i, argv[i]); } printf("\n"); return 0; }
以下は、プログラム実行時の出力です。
$ ./cmdline_basic test1 test2 test3 test4 1234 56789 cmdline args count=7 exe name=./cmdline_basic arg1=test1 arg2=test2 arg3=test3 arg4=test4 arg5=1234 arg6=56789
上記の出力では、引数の合計数が値「7」を保持する main() の「argc」パラメーターによって内部的に維持されていることがわかります (1 つの引数は実行可能ファイル名で、「6」はプログラムに渡される引数です)。引数の値は、文字列の配列である main() の「argv」パラメータに格納されます。ここで、main() 関数は各引数の値を文字列として格納します。 「argv」配列を反復処理すると、プログラムで渡されたすべての引数を取得できることがわかります。
プログラム内の環境変数を処理する追加機能を提供する main () 関数の宣言がもう 1 つあります。同様に、argv[] 配列で維持される引数と同様に、main() 関数には、すべてのシステム環境変数を main() 関数パラメーターとして使用できる文字列の配列に維持する内部機能があります。以下に宣言を示します。
int main (int argc, char *argv[], char **envp)
以下に、コマンドライン引数と環境変数を使用した作業プログラムを示します。
#include <stdio.h> int main (int argc, char *argv[], char **env_var_ptr) { int i=0; printf("\ncmdline args count=%d", argc); /* First argument is executable name only */ printf("\nexe name=%s", argv[0]); for (i=1; i< argc; i++) { printf("\narg%d=%s", i, argv[i]); } i=0; while (*env_var_ptr != NULL) { i++; printf ("\nenv var%d=>%s",i, *(env_var_ptr++)); } printf("\n"); return 0; }
上記のプログラムの出力を以下に示します。
$ ./env test1 test2 cmdline args count=3 exe name=./env arg1=test1 arg2=test2 env var1=>SSH_AGENT_PID=1575 env var2=>KDE_MULTIHEAD=false env var3=>SHELL=/bin/bash env var4=>TERM=xterm env var5=>XDG_SESSION_COOKIE=5edf27907e97deafc70d310550995c84-1352614770.691861-1384749481 env var6=>GTK2_RC_FILES=/etc/gtk-2.0/gtkrc:/home/sitaram/.gtkrc-2.0:/home/sitaram/.kde/share/config/gtkrc-2.0 env var7=>KONSOLE_DBUS_SERVICE=:1.76 env var8=>KONSOLE_PROFILE_NAME=Shell env var9=>GS_LIB=/home/sitaram/.fonts env var10=>GTK_RC_FILES=/etc/gtk/gtkrc:/home/sitaram/.gtkrc:/home/sitaram/.kde/share/config/gtkrc env var11=>WINDOWID=29360154 env var12=>GNOME_KEYRING_CONTROL=/run/user/sitaram/keyring-2Qx7DW env var13=>SHELL_SESSION_ID=f7ac2d9459c74000b6fd9b2df1d48da4 env var14=>GTK_MODULES=overlay-scrollbar env var15=>KDE_FULL_SESSION=true env var16=>http_proxy=http://10.0.0.17:8080/ env var17=>USER=sitaram env var18=>LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36: env var19=>XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session0 env var20=>XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0 env var21=>SSH_AUTH_SOCK=/tmp/ssh-kIFY5HttOJxe/agent.1489 env var22=>ftp_proxy=ftp://10.0.0.17:8080/ env var23=>SESSION_MANAGER=local/Sitaram:@/tmp/.ICE-unix/1716,unix/Sitaram:/tmp/.ICE-unix/1716 env var24=>DEFAULTS_PATH=/usr/share/gconf/kde-plasma.default.path env var25=>XDG_CONFIG_DIRS=/etc/xdg/xdg-kde-plasma:/etc/xdg env var26=>DESKTOP_SESSION=kde-plasma env var27=>PATH=/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games env var28=>PWD=/home/sitaram/test_progs/cmdline env var29=>socks_proxy=socks://10.0.0.17:8080/ env var30=>KONSOLE_DBUS_WINDOW=/Windows/1 env var31=>KDE_SESSION_UID=1000 env var32=>LANG=en_IN env var33=>GNOME_KEYRING_PID=1478 env var34=>MANDATORY_PATH=/usr/share/gconf/kde-plasma.mandatory.path env var35=>UBUNTU_MENUPROXY=libappmenu.so env var36=>KONSOLE_DBUS_SESSION=/Sessions/1 env var37=>https_proxy=https://10.0.0.17:8080/ env var38=>GDMSESSION=kde-plasma env var39=>SHLVL=1 env var40=>HOME=/home/sitaram env var41=>COLORFGBG=15;0 env var42=>KDE_SESSION_VERSION=4 env var43=>LANGUAGE=en_IN:en env var44=>XCURSOR_THEME=Oxygen_White env var45=>LOGNAME=sitaram env var46=>XDG_DATA_DIRS=/usr/share/kde-plasma:/usr/local/share/:/usr/share/ env var47=>DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-mnJhMvd4jG,guid=435ddd41500fd6c5550ed8d2509f4374 env var48=>LESSOPEN=| /usr/bin/lesspipe %s env var49=>PROFILEHOME= env var50=>XDG_RUNTIME_DIR=/run/user/sitaram env var51=>DISPLAY=:0 env var52=>QT_PLUGIN_PATH=/home/sitaram/.kde/lib/kde4/plugins/:/usr/lib/kde4/plugins/ env var53=>LESSCLOSE=/usr/bin/lesspipe %s %s env var54=>XAUTHORITY=/tmp/kde-sitaram/xauth-1000-_0 env var55=>_=./env env var56=>OLDPWD=/home/sitaram/test_progs $
上記の出力では、すべてのシステム環境変数を main() 関数の 3 番目のパラメーターとして取得できることがわかります。これらはプログラム内で走査され、出力に表示されます。
引数をプログラムおよび操作するためのコマンド ライン引数の受け渡し
以下は、コマンドライン引数で動作するプログラムです。
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { int i=0; int d; float f; long int l; FILE *file = NULL; printf("\ncmdline args count=%d", argc); /* First argument is executable name only */ printf("\nexe name=%s", argv[0]); for (i=1; i< argc; i++) { printf("\narg%d=%s", i, argv[i]); } /* Conversion string into int */ d = atoi(argv[1]); printf("\nargv[1] in intger=%d",d); /* Conversion string into float */ f = atof(argv[1]); printf("\nargv[1] in float=%f",f); /* Conversion string into long int */ l = strtol(argv[2], NULL, 0); printf("\nargv[2] in long int=%ld",l); /*Open file whose path is passed as an argument */ file = fopen( argv[3], "r" ); /* fopen returns NULL pointer on failure */ if ( file == NULL) { printf("\nCould not open file"); } else { printf("\nFile (%s) opened", argv[3]); /* Closing file */ fclose(file); } printf("\n"); return 0; }
上記のプログラムの出力を以下に示します。
$ ./cmdline_strfunc 1234test 12345678 /home/sitaram/test_progs/cmdline/cmdline_strfunc.c cmdline args count=4 exe name=./cmdline_strfunc arg1=1234test arg2=12345678 arg3=/home/sitaram/test_progs/cmdline/cmdline_strfunc.c argv[1] in intger=1234 argv[1] in float=1234.000000 argv[2] in long int=12345678 File (/home/sitaram/test_progs/cmdline/cmdline_strfunc.c) opened
上記の出力では、コマンド ライン引数をプログラムで操作できることがわかります。すべての引数は、プログラムに示されているように、integer、float、long に変換できる文字列として取得されます。プログラムからファイルへの処理操作で使用できる任意のファイルのパスとして渡された場合、任意の文字列でさえ、そのファイルです。上記のプログラムを見ると、(/home/sitaram/test_progs/cmdline/cmdline_strfunc.c) ファイル パスがコマンド ライン引数として渡され、プログラム内でファイルを開いたり閉じたりするために使用されます。
Getopt() API
コマンド ライン引数について詳しく調べると、非常に強力な API である getopt() があります。これにより、プログラマはコマンド ライン オプションを解析しやすくなります。プログラマーは、必須またはオプションのコマンド ライン オプションのリストを getopt() に渡すことができます。プログラムが予期するコマンドラインオプションに従って、コマンドラインオプションが有効か無効かを判断できます。 「optarg、optopt、opterr」のような getopt() 固有の内部変数はほとんどありません
- オプターグ :コマンドラインの有効なオプションの引数へのポインタを含みます
- オプト :必須のコマンド ライン オプションがない場合、コマンド ライン オプションが含まれます
- Opterr :無効なオプションが指定された場合、または必須のコマンド ライン オプションの値が指定されていない場合は、0 以外に設定します
以下は、コマンド ライン オプションの解析を理解するための基本的なプログラムです。
#include <stdio.h> #include <unistd.h> int main (int argc, char *argv[]) { int opt = 0; char *in_fname = NULL; char *out_fname = NULL; while ((opt = getopt(argc, argv, "i:o:")) != -1) { switch(opt) { case 'i': in_fname = optarg; printf("\nInput option value=%s", in_fname); break; case 'o': out_fname = optarg; printf("\nOutput option value=%s", out_fname); break; case '?': /* Case when user enters the command as * $ ./cmd_exe -i */ if (optopt == 'i') { printf("\nMissing mandatory input option"); /* Case when user enters the command as * # ./cmd_exe -o */ } else if (optopt == 'o') { printf("\nMissing mandatory output option"); } else { printf("\nInvalid option received"); } break; } } printf("\n"); return 0; }
上記のプログラムの出力は、コマンド ライン オプションをいくつか組み合わせて以下に示します。
Case1: $ ./cmdline_getopt -i /tmp/input -o /tmp/output Input option value=/tmp/input Output option value=/tmp/output Case2: $ ./cmdline_getopt -i -o /tmp/output Input option value=-o Case3: $ ./cmdline_getopt -i ./cmdline_getopt: option requires an argument -- 'i' Missing mandatory input option Case4: $ ./cmdline_getopt -i /tmp/input -o ./cmdline_getopt: option requires an argument -- 'o' Input option value=/tmp/input Missing mandatory output option Case5: $ ./cmdline_getopt -k /tmp/input ./cmdline_getopt: invalid option -- 'k' Invalid option received
上記のプログラムでは、「i」と「o」は、getopt() API を使用するプログラムの必須の入出力コマンド ライン オプションとして使用されます。
上記のプログラムで実行される各ケースの基本的な説明が表示されます:
- Case1 では、両方の必須コマンド ライン オプションとその引数が提供され、プログラムのスイッチ条件の最初の 2 つのケースで適切に処理されます。
- ケース 2 では、必須の入力オプションの値が指定されていませんが、getopt() が十分にインテリジェントではなく、「I」コマンド ライン オプションの値として「-o」と見なされていることがわかります。これは getopt() のエラー ケースではありませんが、プログラマー自身がそのようなケースを処理するためのインテリジェンスを追加できます。
- ケース 3 では、コマンド ライン オプションのみが値なしで指定されており、これは必須オプションであるため、この場合、getopt() は「?」を返し、「optopt」変数が「i」に設定されて、必須入力オプションの値が正しいことを確認します。行方不明。
- ケース 4 では、必須の出力オプションの値がありません。
- ケース 5 では、必須またはオプションのコマンド ライン オプションではない、無効なコマンド ライン オプションが指定されています。この場合、getopt() は「?」を返し、optopt は設定されません。これは、getopt() が想定していない未知の文字であるためです。