質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

1301閲覧

ダブルポインタの使い方

SUNMOON_14

総合スコア20

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2021/07/11 09:52

編集2021/07/11 13:24

状況

現在,C言語にて簡易シェルを作成しようとしています.
以下の変数を用います.

C

1 char command_buffer[BUFLEN]; // コマンド用のバッファ 2 char *args[MAXARGNUM]; // 引数へのポインタの配列

適切に引数の切り出しを行い,
args[0]にはコマンドを表すchar型の配列へのポインタ,args[1], …にはn個のコマンドの引数を表すchar型の配列へのポインタが格納されており,args[n+1]にはNULLが格納されています.
(と理解しています.)

質問

このとき,args[1]が指す文字列を得るにはどのようにプログラムを書けばよいでしょうか?

例(自作cd):

C

1 if(chdir(args[1]) != 0) { 2 fprintf(stderr, "No Such Directory\n"); 3 }

のようにして入力をcd /homeなどとすると,条件分岐に引っ掛かってしまいます(chdir(args[1]) = -1となるということ).
おそらくargs[1]などとしてはいけないのだと推測します.
ls -lなどのコマンドは使えているため,入力→切り出し→格納などはうまくいっているものと考えられます.

配布されたコードを用いていることと,
ls -lを与えたときに通る行である,

C

1 execvp(*args, args);

が正常に動作しているためです.

配布されたプログラム

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <sys/types.h> 4#include <unistd.h> 5#include <sys/wait.h> 6#include <string.h> 7 8#define BUFLEN 1024 9#define MAXARGNUM 256 10 11int parse(char [], char *[]); 12void execute_command(char *[], int); 13 14int main(int argc, char *argv[]) 15{ 16 char command_buffer[BUFLEN]; 17 char *args[MAXARGNUM]; 18 int command_status; 19 20 for(;;) { 21 22 printf("Command : "); 23 24 if(fgets(command_buffer, BUFLEN, stdin) == NULL) { 25 printf("\n"); 26 continue; 27 } 28 29 command_status = parse(command_buffer, args); 30 31 if(command_status == 2) { 32 printf("done.\n"); 33 exit(EXIT_SUCCESS); 34 } else if(command_status == 3) { 35 continue; 36 } 37 38 execute_command(args, command_status); 39 } 40 41 return 0; 42} 43 44int parse(char buffer[], 45 char *args[]) 46{ 47 int arg_index; 48 int status; 49 50 arg_index = 0; 51 status = 0; 52 53 *(buffer + (strlen(buffer) - 1)) = '\0'; 54 55 if(strcmp(buffer, "exit") == 0) { 56 57 status = 2; 58 return status; 59 } 60 61 while(*buffer != '\0') { 62 63 while(*buffer == ' ' || *buffer == '\t') { 64 *(buffer++) = '\0'; 65 } 66 67 if(*buffer == '\0') { 68 break; 69 } 70 71 args[arg_index] = buffer; 72 ++arg_index; 73 74 while((*buffer != '\0') && (*buffer != ' ') && (*buffer != '\t')) { 75 ++buffer; 76 } 77 } 78 79 args[arg_index] = NULL; 80 81 if(arg_index > 0 && strcmp(args[arg_index - 1], "&") == 0) { 82 83 --arg_index; 84 args[arg_index] = '\0'; 85 status = 1; 86 87 } else { 88 89 status = 0; 90 91 } 92 93 if(arg_index == 0) { 94 status = 3; 95 } 96 97 return status; 98} 99 100void execute_command(char *args[], 101 int command_status) 102{ 103 int pid; 104 int status; 105 [ your program ] 106 return; 107}

以下のように,argsにはきちんと格納されていることが分かりました.

> cd /home args[0] = [cd] args[1] = [/home]

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

itagagaki

2021/07/11 10:03

> args[0]が指す文字列を得るには この質問の意味がよくわかりません。 args[0]に格納されているポインタが文字列の先頭アドレスなら、そのアドレスから始まる'\0'までのデータが文字列ですよね。そういうことではないんですか? > 入力をcd /homeなどとすると,条件分岐に引っ掛かってしまいます これも意味がよくわかりません。その場合 chdir("/home") を実行するんですよね? 条件分岐に「引っかかる」とはどういう意味ですか?
pepperleaf

2021/07/11 10:07

多分、ダブルポインタの問題ではなく、chdir()関数の問題ではないかと。 失敗した時の errnoはどうなってるのでしょうか。 あとは、良くある話で、読み込んだ文字列が間違えてるとか、、。
angel_p_57

2021/07/11 10:15

> ls -lなどのコマンドは使えているため,入力→切り出し→格納などはうまくいっているものと考えられます. その判断が正しいとするには根拠薄弱です。 もっと直接的に、args[] に想定した値が入っているかどうか、デバッガで確認するか、最悪 printf で出力するなりして確かめるのが先決です。
SUNMOON_14

2021/07/11 10:21

> もっと直接的に、args[] に想定した値が入っているかどうか、デバッガで確認するか、最悪 printf で出力するなりして確かめるのが先決です。 printf("%s", args[0]); ということですか?出力方法が分かりません.
SUNMOON_14

2021/07/11 10:26

itagagakiさん,前半はおっしゃる通りです. 「cd /home」を与えても,chdir(args[1]) が -1となるために質問しました.
itagagaki

2021/07/11 10:40

chdir(args[1]) が -1となることと質問の args[0] とは何の関係があるのですか?
SUNMOON_14

2021/07/11 10:44

args[0]が得られたらargs[1]も得られませんか? args[n] (n ∈ ℕ)を得る方法全般が分かりません.
jimbe

2021/07/11 10:59

コードやお話がばらばらで、何が起きているのか、何をしたいのかがとても分かり難いです。 > 配布されたコードを用いている ということですので、そのコード全体をご提示頂けませんか。
itagagaki

2021/07/11 11:04

> args[0]が得られたらargs[1]も得られませんか? 教科書の1ページ目から勉強し直すべきかな思います。
SUNMOON_14

2021/07/11 11:36

jimbeさん,ソースコードを追加しました.
jimbe

2021/07/11 11:46

ソースコードありがとうございます。 ぱっと見ですが >ls -lを与えたときに通る行である,execvp(*args, args); というのが見当たりませんが… chdir ~ もありません。 おそらく execute_command 関数の "[ your program ]" のところに yt.nb さんがそれらのコードを追加されたのだと思いますが、その部分も頂けませんか。
SUNMOON_14

2021/07/11 12:09 編集

一時的にこちらに公開します.よろしくお願いいたします.
angel_p_57

2021/07/11 12:06

> > もっと直接的に、args[] に想定した値が入っているかどうか、デバッガで確認するか、最悪 printf で出力するなりして確かめるのが先決です。 > printf("%s", args[0]); ということですか?出力方法が分かりません. 流石にそれは、今回の質問を行うだけの前提となる能力・知識不足と判断します。 大学の課題というお話のようですので、私は回答を遠慮させていただきます。
jimbe

2021/07/11 12:13

execute_command と my_cd のコードを頂きました。 ですが、まだコンパイルが出来ないようです。 command_num や command_str 変数はどうなっているでしょうか。 my_cd 関数が execute_command から直接呼ばれていない所を見ると、 例えば command_str[0]="cd" 、 command_func[0]=my_cd となっていて (*command_func[i])() によって呼ばれるように思えますが、my_cd 関数の定義には char *args[] がパラメータとしてありますが呼び出す (*command_func[i])() にはパラメータがありません。この辺りは如何ですか。
pepperleaf

2021/07/11 12:14

過去の質問を見ると、解決済みで、質問内容が抹消されているようですが、こちらも解決した時、抹消される?
SUNMOON_14

2021/07/11 12:18 編集

大学の課題でソースコードその物を出したため消しました.大学から配布されたコードはこちらに載せ,オリジナルの部分は別のサイトにコードを載せているので,消しません.
SUNMOON_14

2021/07/11 12:18

jimbeさん, グローバル変数は載せました!プロトタイプ宣言は適当に行ってくださいm(__)m
jimbe

2021/07/11 12:27

ありがとうございます。 command_str や command_func については想像通りだったようですね。 ですので、気になるのは cd コマンドを実行しようとした時 command_func[0] を使って my_cd が呼ばれますが、先に書きました通り、 my_cd 関数の定義は void my_cd(char *args[]){~} とパラメータが有りますのに、呼び出しは (*command_func[i])(); とパラメータが無い点です。 (my_cd 内で) args[1] が取れるか…との趣旨でご質問されていますが、そもそも my_cd にパラメータを渡していないのではないでしょうか。
SUNMOON_14

2021/07/11 12:59

そういうことですか!
SUNMOON_14

2021/07/11 12:59

知らないものを使うのはよくないですね… 関数ポインタを初めて使いました。
SUNMOON_14

2021/07/11 13:00

どのように変更すればよいでしょうか?
jimbe

2021/07/11 13:12

簡単に言えば、 (*command_func[i])(args); とすることです。 ちょっと手元にコンパイル環境がありませんので確認できませんが、プロトタイプ宣言や command_func の定義の修正等の整合性(コンパイルエラー・ワーニング)を取る必要があるかもしれません。 また、関数ポインタが初めてということであれば、"c 関数ポインタ パラメータ” などと検索してみるのも手かと思います。 それともう一つ、このコードでは "cd" の実行を「 fork した子プロセスで行う」ようになっていますが、子プロセスでカレントディレクトリを変更しても、親プロセスのカレントディレクトリは変わらないような気がします。 "cd" は親プロセスで実行しないといけないのではないでしょうか。
SUNMOON_14

2021/07/11 14:02

まさにそれで解決しました!本当にありがとうございます!!!
guest

回答3

0

自己解決

ご回答にもあった通り、ダブルポインタが直接的な原因ではありませんでした。

・void (*command_func[]) ()→void (*command_func[]) (char *[]) とした点
・親プロセスで自作cdを実行した点

により解決しました。ご助言いただいた皆様、ありがとうございました。

投稿2021/07/11 14:01

編集2021/07/11 14:12
SUNMOON_14

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2021/07/11 14:45

おめでとうございます。 > 知らないものを使うのはよくない と仰られていましたが、そんなことはありません。 c で難しいと言われるポインタ、それも関数のポインタを初めて使い、そしてパラメータを渡すことも出来るようになられたのですから、経験値は確実に増えたと思います。 また、要所に printf で変数の表示を入れることで内容を確認するという手法を皆様仰られています。これもデバッグ方法としては基本かつ重要なことで、あまり行っておられなかったようですので、これも経験ではないでしょうか。 おつかれさまでした。
guest

0

args[0]にはコマンドを表すchar型の配列へのポインタ,args[1], …にはn個のコマンドの引数を表すchar型の配列へのポインタが格納されており,args[n+1]にはNULLが格納されています.

(と理解しています.)

まずその理解が正しいか否か、片っ端からprintして確認してはいかがでしょうか。

投稿2021/07/11 10:48

episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SUNMOON_14

2021/07/11 10:52

printfで出力しようとすると実行が少し固まって,再度コマンド入力待機状態に戻ります. [実行例] > ls -l total 40 -rwxr-xr-x 1 runner runner 13384 Jul 11 10:16 debug -rw-r--r-- 1 runner runner 0 Jul 4 09:41 main.c -rwxr-xr-x 1 runner runner 13384 Jul 11 09:36 simple_shell -rw-r--r-- 1 runner runner 7846 Jul 11 10:16 simple_shell.c > cd (ここで1秒固まる) > printfの書き方を教えてください
episteme

2021/07/11 10:55

for ( int i = 0; args[i] != NULL; ++i ) { printf("args[%d] = [%s]\n", i, args[i]); }
SUNMOON_14

2021/07/11 11:43

【そのprintfを入れたとき】 > ls -l total 40 -rwxr-xr-x 1 runner runner 13384 Jul 11 11:41 debug -rw-r--r-- 1 runner runner 0 Jul 4 09:41 main.c -rwxr-xr-x 1 runner runner 13384 Jul 11 09:36 simple_shell -rw-r--r-- 1 runner runner 7906 Jul 11 11:40 simple_shell.c > cd > exit done. 【printfを入れなかったとき】 > ls -l total 40 -rwxr-xr-x 1 runner runner 13384 Jul 11 11:42 debug -rw-r--r-- 1 runner runner 0 Jul 4 09:41 main.c -rwxr-xr-x 1 runner runner 13384 Jul 11 09:36 simple_shell -rw-r--r-- 1 runner runner 7810 Jul 11 11:41 simple_shell.c > cd No Such Directory > exit done. printfを入れるとその後の処理が一切行われないのですが,何が起こっていますか?
episteme

2021/07/11 12:17

何が起こってるかを調べるのはアナタ。
SUNMOON_14

2021/07/11 12:20

親身になってご回答いただける方のみで結構です
episteme

2021/07/11 12:21

そりゃそうでしょう、僕らはコードが見えてないんだから「作った通りに動いてる」としか言えんので。
SUNMOON_14

2021/07/11 12:21

コード追記していますよ
episteme

2021/07/11 12:24

parse()を呼んだあと、args[]をprintしてみたんですよね? 期待通りの文字列がプリントされましたか?
SUNMOON_14

2021/07/11 13:08

> cd /home args[0] = [cd] args[1] = [/home] きちんと入っていることは確認できました.
guest

0

このとき,args[0]が指す文字列を得るにはどのようにプログラムを書けばよいでしょうか?

なら、args[0]じゃないの?
なんでargs[1]になるの?

投稿2021/07/11 10:43

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SUNMOON_14

2021/07/11 10:45

どちらでも良いです.
y_waiwai

2021/07/11 10:47

この質問文だけでは、なにを実現しようとしているってのが見えません なにをしたいんでしょうか
SUNMOON_14

2021/07/11 10:48

if(args[1] == NULL) { args[1] = getenv("HOME"); } else { if(chdir(args[1]) != 0) { fprintf(stderr, "No Such Directory\n"); } } 「cd」と与えた場合,一つ目の条件分岐に引っ掛からないです.
SUNMOON_14

2021/07/11 10:49

コマンドプロンプトで「cd /home」と与えても,「No Such Directory」と出力されます… カレントディレクトリは/home/runner/program2です.
y_waiwai

2021/07/11 10:52

/home というディレクトリが存在しない、ってことです。
SUNMOON_14

2021/07/11 11:30

このプログラムを立ち上げない、本物のシェルでは以下のように動作します。 ~/program2$ cd /home /home$
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問