12.15.2017

getopt_long()測試方法-B

基於上一篇getopt_long()測試方法-A的文章,自己是建立在使用者的角度,來分析這樣的一個函式應該要如何設計。

雖然沒有辦法做到很完美,例如像"ps aux""rpm -ivh"這種指令的選項解譯,但是還是有符合大部份Linux系統下的指令需求。

getopt_long()函式所傳進去的第4個引數const struct option *longopts,它是一個option的結構陣列,option結構的第4個成員int val; 的用法,和第1個成員const char *name; 相呼應。換句話說,就是當使用者輸入長選項"--help"時,就等同於短選項的"-H"

問題來了,option結構的第2個成員int has_arg; 是決定這個長選項是否要帶參數。而getopt_long()函式所傳進去的第3個引數const char *optstring; 是定義短選項的字元,若字元後面有接冒號 (:)就代表這個短選項後面需要接參數;沒有接冒號就代表不需要接參數。

Question: 「如果第3個引數const char *optstring; 和第4個引數const struct option *longopts的設定『發生衝突』,會發生什麼事?

長選項的option結構的第2個成員int has_arg; 有3種設定方式:沒有參數 (no_argument)必有參數 (required_argument)可有參數 (optional_argument)短選項就只有2種設定方式:沒有參數 ()必有參數 (:)

所以,如此又多了3 * 2 = 6種測試方式!我們先來看最理想的狀況:

Step VIII: 長選項設定為沒有參數 (no_argument)短選項也設定為沒有參數 ()
57. ./main --help
58. ./main --help 1234
59. ./main -H
60. ./main -H 1234
[root@localhost]# 
[root@localhost]# ./main --help
You got a short option argv[1]: "-H"

[root@localhost]# 
[root@localhost]# ./main --help 1234 
You got a short option argv[1]: "-H", but DOESN'T need argument argv[2]: 1234 

[root@localhost]# 
[root@localhost]# ./main -H
You got a short option argv[1]: "-H"

[root@localhost]# 
[root@localhost]# ./main -H 1234 
You got a short option argv[1]: "-H", but DOESN'T need argument argv[2]: 1234 

[root@localhost]# 
[root@localhost]# 
這一部份的測試結果,若長選項"--help"有找到相對應的短選項"-H",執行結果會以短選項為優先。

Step IX: 長選項設定為必有參數 (required_argument)短選項也設定為必有參數 (:)
61. ./main --delete
62. ./main --delete 1234
63. ./main -D
64. ./main -D 1234
[root@localhost]# 
[root@localhost]# ./main --delete 
./main: option '--delete' requires an argument
The long option argv[1]: "--delete" need an argument!! 

[root@localhost]# 
[root@localhost]# ./main --delete 1234 
You got a short option argv[1]: "-D" with an argument: argv[2]: "1234" 

[root@localhost]# 
[root@localhost]# ./main -D 
./main: option requires an argument -- 'D'
The short option argv[1]: "-D" need an argument!! 

[root@localhost]# 
[root@localhost]# ./main -D 1234 
You got a short option argv[1]: "-D" with an argument: argv[2]: "1234" 

[root@localhost]# 
[root@localhost]# 
不論是長選項或是短選項,應帶參數而未帶參數,getopt_long()函式就直接回傳問號'?'字元;若有帶參數,就直接對應到短選項的設定。

Step X: 長選項設定為沒有參數 (no_argument)短選項設定為必有參數 (:)
65. ./main --count
66. ./main --count 1234
67. ./main -c
68. ./main -c 1234
[root@localhost]# 
[root@localhost]# ./main --count 
程式記憶體區段錯誤 (core dumped)
[root@localhost]# 
[root@localhost]# ./main --count 1234 
程式記憶體區段錯誤 (core dumped)
[root@localhost]# 
[root@localhost]# ./main -c 
./main: option requires an argument -- 'c'
The short option argv[1]: "-c" need an argument!! 

[root@localhost]# 
[root@localhost]# ./main -c 1234 
You got a short option argv[1]: "-c" with an argument: argv[2]: "1234" 

[root@localhost]# 
[root@localhost]# 
當執行長選項時,就會發生「程式記憶體區段錯誤 (core dumped)」的錯誤訊息;若執行短選項時,則是正常執行。

Step XI: 長選項設定為必有參數 (required_argument)短選項設定為沒有參數 ()
69. ./main --create
70. ./main --create 1234
71. ./main -C
72. ./main -C 1234
[root@localhost]# 
[root@localhost]# ./main --create
./main: option '--create' requires an argument
The long option argv[1]: "--create" need an argument!! 

[root@localhost]# 
[root@localhost]# ./main --create 1234 
You got a short option argv[2]: "-C"

[root@localhost]# 
[root@localhost]# ./main -C 
You got a short option argv[1]: "-C"

[root@localhost]# 
[root@localhost]# ./main -C 1234 
You got a short option argv[1]: "-C", but DOESN'T need argument argv[2]: 1234 

[root@localhost]# 
[root@localhost]# 
這個執行結果就有意思了!

a). 當長選項設定為必有參數而未輸入參數時,getopt_long()函式會回傳0,因為它有找到相對應的長選項;

b). 當長選項設定為必有參數而輸入參數時,getopt_long()函式會回傳相對應的短選項字元;但短選項的字元設定為沒有參數,所以就不會抓取輸入長選項時所帶進來的參數。

c). 當短選項設定為沒有參數時,完全不影響長選項的設定。

Step XII: 長選項設定為可有參數 (optional_argument)短選項設定為沒有參數 ()
73. ./main --interface
74. ./main --interface 1234
75. ./main -I
76. ./main -I 1234
[root@localhost]# 
[root@localhost]# ./main --interface 
You got a short option argv[1]: "-I"

[root@localhost]# 
[root@localhost]# ./main --interface 1234 
You got a short option argv[1]: "-I", but DOESN'T need argument argv[2]: 1234 

[root@localhost]# 
[root@localhost]# ./main -I 
You got a short option argv[1]: "-I"

[root@localhost]# 
[root@localhost]# ./main -I 1234 
You got a short option argv[1]: "-I", but DOESN'T need argument argv[2]: 1234 

[root@localhost]# 
[root@localhost]# 
輸入短選項時,並沒有什麼問題;但輸入長選項時,則是直接對應到短選項。因為短選項設定為沒有參數,所以不管長選項有無帶參數,都不會抓取。

Step XIII: 長選項設定為可有參數 (optional_argument)短選項設定為必有參數 (:)
73. ./main --list
74. ./main --list 1234
75. ./main -L
76. ./main -L 1234
[root@localhost]# 
[root@localhost]# ./main --list 
程式記憶體區段錯誤 (core dumped)
[root@localhost]# 
[root@localhost]# ./main --list 1234 
程式記憶體區段錯誤 (core dumped)
[root@localhost]# 
[root@localhost]# ./main -L 
./main: option requires an argument -- 'L'
The short option argv[1]: "-L" need an argument!! 

[root@localhost]# 
[root@localhost]# ./main -L 1234 
You got a short option argv[1]: "-L" with an argument: argv[2]: "1234" 

[root@localhost]# 
[root@localhost]# 
因為短選項設定為必有參數,雖然長選項設定為可有參數,但是當短選項對應到長選項時,依然會發生「程式記憶體區段錯誤 (core dumped)」的問題。

綜合以上Step VIII ~ XIII的實驗結果可得知:

a). 長選項如果有相對應的短選項時,短選項的設定優先權會大於長選項

b). 例如:當短選項設定為沒有參數 ()時,無論長選項是設定為必有參數 (required_argument)或是可有參數 (optional_argument)時,短選項都不會去抓取長選項的參數。

c). 反之,當短選項設定為必有參數 (:)時,無論長選項是設定為沒有參數 (no_argument)或是可有參數 (optional_argument)而沒帶參數時,都會發生「程式記憶體區段錯誤 (core dumped)」的問題。

Answer: 簡而言之,當長選項和短選項的參數設定發生衝突時,getopt_long()函式並沒有解決這樣的問題;反而是程式設計師們應自行注意,不應讓這樣的衝突發生。

在一開始的範例程式碼最後,還多加了3個函式:pid_t writePID(const char *PATH); pid_t readPID(const char *PATH); 和int killPID(const char *PATH); 這3個函式也是在Linux Programming下常看到的。當我們執行一支常註程式時,通常都會記下自己在系統中的PID (Process ID),所以會有一個writePID()函式;倘若有別支程序需要送一個訊號 (Signal)和這一支程式溝通時,就需要用readPID()函式;當我們的常註程式要結束時,也要記得刪除自己的PID,就用killPID()函式。

相關文章:Linux C getopt_long()

沒有留言:

張貼留言