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

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

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

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

シェル

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

プログラミング言語

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

Q&A

1回答

1792閲覧

C言語 シェル作成 ディレクトリスタックの実装 線形リスト プロセス管理

yuto1123581321

総合スコア16

C

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

シェル

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

プログラミング言語

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

0グッド

0クリップ

投稿2019/07/24 15:50

前提・実現したいこと

学校の課題でC言語でシェルを作っています.その中のディレクトリの履歴をpushedコマンドでスタックに格納してdirsコマンドでスタックの上から表示させたいです.スタックは単方向リストで作っています.
(外部コマンドなどは使用不可)

字数制限によりコマンド解析のparse関数を消しています.execute_command関数でのargs[i]に第i引数のポインタが入る仕組みです.(iは0以上の整数)

発生している問題・エラーメッセージ

実行してみたところprintfを挟んだ実行から連結リストは生成されているようなのですが,ディレクトリを移動してpushedコマンドを打つとそれまでのディレクトリの履歴が全て書き換わってしまいます.(最後にうったpushedのカレントディレクトリに書き換わります.)おそらく,よく理解できでない子プロセス親プロセスに関わる何かが悪さしているのではないかと考えています.
現在作成途中ですのでcdコマンドはホームディレクトリに移動するものしか作っていません.後はpwdコマンドで現在のカレントディレクトリを表示させる機能です.
構造体の定義は下に説明させていただいてます.

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #define BUFLEN 1024 /* コマンド用のバッファの大きさ */ #define MAXARGNUM 256 /* 最大の引数の数 */ #define MAXBUF 256 /* * ローカルプロトタイプ宣言 */ typedef struct dirstack{ char *name; struct dirstack* next; }dir_st; typedef struct stacklist { dir_st* Top; }top_node; int parse(char [], char *[]); void execute_command(char *[], int); void changedir(char *[]); void dirpush(char **); void dirpop(char **); char* GetCd(char*); void printdir(dir_st*); dir_st* initnode(char*); top_node* inittop(char*); void push(top_node*,char*); static char *command_list[]={ "cd", "pwd","pushed","dirs" }; //とりあえずの便利な文字を置く場所 char buf[MAXBUF]; //文字の置き場所 top_node *Top = NULL; /*---------------------------------------------------------------------------- * * 関数名 : main * * 作業内容 : シェルのプロンプトを実現する *--------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { char command_buffer[BUFLEN]; /* コマンド用のバッファ */ char *args[MAXARGNUM]; /* 引数へのポインタの配列 */ int command_status; /* コマンドの状態を表す command_status = 0 : フォアグラウンドで実行 command_status = 1 : バックグラウンドで実行 command_status = 2 : シェルの終了 command_status = 3 : 何もしない */ /* * 無限にループする */ for(;;) { /* * プロンプトを表示する */ printf("Command : "); /* * 標準入力から1行を command_buffer へ読み込む * 入力が何もなければ改行を出力してプロンプト表示へ戻る */ if(fgets(command_buffer, BUFLEN, stdin) == NULL) { printf("\n"); continue; } /* * 入力されたバッファ内のコマンドを解析する * * 返り値はコマンドの状態 */ command_status = parse(command_buffer, args); /* * 終了コマンドならばプログラムを終了 * 引数が何もなければプロンプト表示へ戻る */ if(command_status == 2) { printf("done.\n"); exit(EXIT_SUCCESS); } else if(command_status == 3) { continue; } /* * コマンドを実行する */ execute_command(args, command_status); } return 0; } /*---------------------------------------------------------------------------- * * 関数名 : parse * * 作業内容 : バッファ内のコマンドと引数を解析する * * 引数 : * * 返り値 : コマンドの状態を表す : * 0 : フォアグラウンドで実行 * 1 : バックグラウンドで実行 * 2 : シェルの終了 * 3 : 何もしない * *--------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- * * 関数名 : execute_command * * 作業内容 : 引数として与えられたコマンドを実行する * コマンドの状態がフォアグラウンドならば、コマンドを * 実行している子プロセスの終了を待つ * バックグラウンドならば子プロセスの終了を待たずに * main 関数に返る(プロンプト表示に戻る) * * 引数 : * * 返り値 : * * 注意 : * *--------------------------------------------------------------------------*/ void execute_command(char *args[], /* 引数の配列 */ int command_status) /* コマンドの状態 */ { int pidid; /* プロセスID */ int status; /* 子プロセスの終了ステータス */ pid_t pid; pid_t wait_pid; char *command; int state=0; char *crentdir; /* * * 生成できたかを確認し、失敗ならばプログラムを終了する */ pid = fork(); if(pid == -1){ exit(-1); }else if(pid == 0){ /******** Your Program ********/ if(strcmp(*(command_list+1),*args)==0){ GetCd(crentdir); exit(EXIT_SUCCESS); } } /* * 子プロセスの場合には引数として与えられたものを実行する * * 引数の配列は以下を仮定している * ・第1引数には実行されるプログラムを示す文字列が格納されている * ・引数の配列はヌルポインタで終了している */ /******** Your Program ********/ /* * コマンドの状態がバックグラウンドなら関数を抜ける */ /******** Your Program ********/ /* * ここにくるのはコマンドの状態がフォアグラウンドの場合 * * 親プロセスの場合に子プロセスの終了を待つ */ wait_pid = wait(&state); if(strcmp(*command_list,*args) == 0){ changedir(args); } if(strcmp(*(command_list+2),*args)==0){ //*pushed if(Top==NULL){ Top = inittop(getcwd(buf,sizeof(buf)-1)); }else{ push(Top,getcwd(buf,sizeof(buf)-1)); } } if(strcmp(*(command_list+3),*args)==0){ //dirs printdir(Top->Top); } /******** Your Program ********/ return; } void changedir(char *args[]){ if(*(args+1) == '\0' ){ const char *home = getenv("HOME"); printf("%s\n",home); if(chdir(home) != 0){ printf("ERROR\n"); } chdir(home); } } char *GetCd(char* p){ p = getcwd(buf,sizeof(buf)-1); if(p == NULL){ printf("getcwd error\n"); } printf("%s\n",p); return p; } void printdir(dir_st* node){ if(node != NULL){ printdir(node->next); printf("%s\n",node->name); } } dir_st* initnode(char* name){ dir_st* pNode = NULL; pNode = (dir_st*)malloc(sizeof(dir_st)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->name = name; pNode->next = NULL; return pNode; } top_node* inittop(char* name){ top_node* pNode = NULL; pNode = (top_node*)malloc(sizeof(top_node)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->Top = initnode(name); if(pNode->Top == NULL){ printf("TopError\n"); free(pNode); } printdir(pNode->Top); return pNode; } void push(top_node* Node,char* name){ dir_st* pNode = NULL; if(Node == NULL){ printf("Error\n"); return; } if(Node->Top == NULL){ Node->Top = initnode(name); if(Node->Top == NULL){ printf("pusherror\n"); } return; } pNode = Node->Top; while(pNode->next != NULL){ pNode = pNode->next; } pNode->next = initnode(name); if(pNode->next == NULL){ printf("push error\n"); return; } printdir(Node->Top); return; } /*-- END OF FILE -----------------------------------------------------------*/

試したこと

このコードを実行したところ,以下のような結果になりました.

Command : pushed /Users/myname/Desktop/c Command : pushed /Users/myname/Desktop/c /Users/myname/Desktop/c Command : pushed /Users/myname/Desktop/c /Users/myname/Desktop/c /Users/myname/Desktop/c Command : cd /Users/myname Command : pushed /Users/myname /Users/myname /Users/myname /Users/my-name

問題に関係のあるところははexecute_command関数内のpushedとコメントした部分だと思っています.
まず構造体の定義が,

typedef struct dirstack{ char *name; struct dirstack* next; }dir_st; typedef struct stacklist { dir_st* Top; }top_node;

で,dir_stがノードでありtop_nodeが先頭を指すポインタです.
以下がexecute_command関数です.ここでコマンドの実行をしています.

printdir関数で単方向リストを再帰を使い逆順に表示をしています.
initnode関数で各ノードを作っています.
inittop関数で先頭ノードを生成しています.
push関数でpushしています.pushする内容はcommand_execuでカレントディレクトリを取得しそのデータをpushしています.また先頭ノードは大域変数で宣言しています.
小さい別のプログラムで線形リストのスタック自体は作れていることは確認したので,別のところに問題があると思います.よろしくお願いします.
また親プロセス子プロセスについてよくわかってないので何をどっちで実行すればいいのか教えてくだされば幸いです.

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

Zuishin

2019/07/24 22:53

質問と実行結果が違います。実行結果は問題ないように見えます。
yuto1123581321

2019/07/25 08:31

返信遅くなってしまいすいません 目的とする実行結果は pushedを押した時点でのカレントディレクトリを詰みたいです。 たとえば1回目にpushedをおしたときのカレントディレクトリがAだとして、cdを押してホームに戻ったディレクトリをBだとした場合、 立ち上がった時のカレントディレクトリがAで pushedをおすとAがひょうじされ、 cdでホームに戻ったあと pushedをおすと B A と表示させたいのですが、 最後にpushedをタイプした時のディレクトリBに全て書きかわり、 B B とひょうじされてしまいます・・・
guest

回答1

0

すべてのノードの name が同じ buf の先頭アドレスを指しているからです。

投稿2019/07/25 09:00

Zuishin

総合スコア28660

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問