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

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

ただいまの
回答率

87.34%

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

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,774

score 16

前提・実現したいこと

学校の課題で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/ツールのバージョンなど)

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2019/07/25 07:53

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

    キャンセル

  • yuto1123581321

    2019/07/25 17:31

    返信遅くなってしまいすいません
    目的とする実行結果は
    pushedを押した時点でのカレントディレクトリを詰みたいです。
    たとえば1回目にpushedをおしたときのカレントディレクトリがAだとして、cdを押してホームに戻ったディレクトリをBだとした場合、

    立ち上がった時のカレントディレクトリがAで
    pushedをおすとAがひょうじされ、
    cdでホームに戻ったあと
    pushedをおすと
    B
    A

    と表示させたいのですが、
    最後にpushedをタイプした時のディレクトリBに全て書きかわり、
    B
    B

    とひょうじされてしまいます・・・

    キャンセル

回答 1

+3

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る