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

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

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

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

Q&A

1回答

1174閲覧

c言語でprintfで\nを入れるとなぜか二回出力される

yukikona

総合スコア12

C

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

0グッド

0クリップ

投稿2020/07/29 07:05

編集2022/01/12 10:55

前提・実現したいこと

c言語でサブシェルを作成しており、その中でディレクトリスタックへカレントディレクトリを保存できるようにするpushdコマンドと現在のディレクトリスタックを表示できるようにするdirsコマンドを単方向リストで作成したのですが,dirsコマンドでリストを表示していくときになぜか表示して欲しい数の二倍表示されてしまいます

該当のソースコード

c

1struct stack{ 2 char path[100]; 3 struct stack *next; 4}; 5struct stack Topad; 6struct stack *topad; 7 8topad=&Topad; 9struct stack *newstr; 10newstr = (struct stack*)malloc(sizeof(struct stack)); 11topad->next=newstr; 12/*コードは略す 13コマンドラインが表示され、pushdを入力すると下のpushd_command関数に移動し、ディレクトリスタックへ現在のカレントディレクトリをtopadの次のリストを新規作成し保存する。コマンドラインでdirsを入力するとしたのdirs_command関数に移動し、listがNULLになるまで表示し続ける 14*/ 15 16void pushd_command(char *args[]){ 17 struct stack *list; 18 struct stack *newstr; 19 list=topad->next; 20 char cwd[BUFLEN]; 21 getcwd(cwd,sizeof(cwd)); 22 23 if(list->path[0]=='\0'){ 24 strcpy(list->path,cwd); 25 list->next=NULL; 26 }else{ 27 newstr = (struct stack*)malloc(sizeof(struct stack)); 28 strcpy(newstr->path,cwd); 29 topad->next=newstr; 30 newstr->next=list; 31 } 32 return; 33} 34 35void dirs_command(char *args[]){ 36 struct stack *list; 37 list=topad->next; 38 while(list!=NULL){ 39 printf("%s\n",list->path); 40 list=list->next; 41 } 42 return; 43}

### 全体のソースコード

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/* 9 * 定数の定義 10 */ 11 12#define BUFLEN 1024 /* コマンド用のバッファの大きさ */ 13#define MAXARGNUM 256 /* 最大の引数の数 */ 14 15 16/* 17 * ローカルプロトタイプ宣言 18 */ 19 20int parse(char [], char *[]); 21void execute_command(char *[], int); 22void cd_command(char *[]); 23void pushd_command(char *[]); 24void dirs_command(char *[]); 25void popd_command(char *[]); 26void history_command(char *[]); 27 28struct stack{ 29 char path[100]; 30 struct stack *next; 31}; 32struct stack Topad; 33struct stack *topad; 34 35 36/*---------------------------------------------------------------------------- 37 * 38 * 関数名 : main 39 * 40 * 作業内容 : シェルのプロンプトを実現する 41 * 42 * 引数 : 43 * 44 * 返り値 : 45 * 46 * 注意 : 47 * 48 *--------------------------------------------------------------------------*/ 49 50int main(int argc, char *argv[]) 51{ 52 char command_buffer[BUFLEN]; /* コマンド用のバッファ */ 53 char *args[MAXARGNUM]; /* 引数へのポインタの配列 */ 54 int command_status; /* コマンドの状態を表す 55 command_status = 0 : フォアグラウンドで実行 56 command_status = 1 : バックグラウンドで実行 57 command_status = 2 : シェルの終了 58 command_status = 3 : 何もしない */ 59 topad=&Topad; 60 struct stack *newstr; 61 newstr = (struct stack*)malloc(sizeof(struct stack)); 62 topad->next=newstr; 63 64 65 /* 66 * 無限にループする 67 */ 68 69 for(;;) { 70 71 /* 72 * プロンプトを表示する 73 */ 74 75 printf("Command : "); 76 77 /* 78 * 標準入力から1行を command_buffer へ読み込む 79 * 入力が何もなければ改行を出力してプロンプト表示へ戻る 80 */ 81 82 if(fgets(command_buffer, BUFLEN, stdin) == NULL) { 83 printf("\n"); 84 continue; 85 } 86 87 /* 88 * 入力されたバッファ内のコマンドを解析する 89 * 90 * 返り値はコマンドの状態 91 */ 92 93 command_status = parse(command_buffer, args); 94 95 /* 96 * 終了コマンドならばプログラムを終了 97 * 引数が何もなければプロンプト表示へ戻る 98 */ 99 100 if(command_status == 2) { 101 printf("done.\n"); 102 exit(EXIT_SUCCESS); 103 } else if(command_status == 3) { 104 continue; 105 } 106 107 /* 108 * コマンドを実行する 109 */ 110 111 execute_command(args, command_status); 112 } 113 114 return 0; 115} 116 117/*---------------------------------------------------------------------------- 118 * 119 * 関数名 : parse 120 * 121 * 作業内容 : バッファ内のコマンドと引数を解析する 122 * 123 * 引数 : 124 * 125 * 返り値 : コマンドの状態を表す : 126 * 0 : フォアグラウンドで実行 127 * 1 : バックグラウンドで実行 128 * 2 : シェルの終了 129 * 3 : 何もしない 130 * 131 * 注意 : 132 * 133 *--------------------------------------------------------------------------*/ 134 135int parse(char buffer[], /* バッファ */ 136 char *args[]) /* 引数へのポインタ配列 */ 137{ 138 int arg_index; /* 引数用のインデックス */ 139 int status; /* コマンドの状態を表す */ 140 141 /* 142 * 変数の初期化 143 */ 144 145 arg_index = 0; 146 status = 0; 147 148 /* 149 * バッファ内の最後にある改行をヌル文字へ変更 150 */ 151 152 *(buffer + (strlen(buffer) - 1)) = '\0'; 153 154 /* 155 * バッファが終了を表すコマンド("exit")ならば 156 * コマンドの状態を表す返り値を 2 に設定してリターンする 157 */ 158 159 if(strcmp(buffer, "exit") == 0) { 160 161 status = 2; 162 return status; 163 } 164 165 /* 166 * バッファ内の文字がなくなるまで繰り返す 167 * (ヌル文字が出てくるまで繰り返す) 168 */ 169 170 while(*buffer != '\0') { 171 172 /* 173 * 空白類(空白とタブ)をヌル文字に置き換える 174 * これによってバッファ内の各引数が分割される 175 */ 176 177 while(*buffer == ' ' || *buffer == '\t') { 178 *(buffer++) = '\0'; 179 } 180 181 /* 182 * 空白の後が終端文字であればループを抜ける 183 */ 184 185 if(*buffer == '\0') { 186 break; 187 } 188 189 /* 190 * 空白部分は読み飛ばされたはず 191 * buffer は現在は arg_index + 1 個めの引数の先頭を指している 192 * 193 * 引数の先頭へのポインタを引数へのポインタ配列に格納する 194 */ 195 196 args[arg_index] = buffer; 197 ++arg_index; 198 199 /* 200 * 引数部分を読み飛ばす 201 * (ヌル文字でも空白類でもない場合に読み進める) 202 */ 203 204 while((*buffer != '\0') && (*buffer != ' ') && (*buffer != '\t')) { 205 ++buffer; 206 } 207 } 208 209 /* 210 * 最後の引数の次にはヌルへのポインタを格納する 211 */ 212 213 args[arg_index] = NULL; 214 215 /* 216 * 最後の引数をチェックして "&" ならば 217 * 218 * "&" を引数から削る 219 * コマンドの状態を表す status に 1 を設定する 220 * 221 * そうでなければ status に 0 を設定する 222 */ 223 224 if(arg_index > 0 && strcmp(args[arg_index - 1], "&") == 0) { 225 226 --arg_index; 227 args[arg_index] = '\0'; 228 status = 1; 229 230 } else { 231 232 status = 0; 233 234 } 235 236 /* 237 * 引数が何もなかった場合 238 */ 239 240 if(arg_index == 0) { 241 status = 3; 242 } 243 244 /* 245 * コマンドの状態を返す 246 */ 247 248 return status; 249} 250 251/*---------------------------------------------------------------------------- 252 * 253 * 関数名 : execute_command 254 * 255 * 作業内容 : 引数として与えられたコマンドを実行する 256 * コマンドの状態がフォアグラウンドならば、コマンドを 257 * 実行している子プロセスの終了を待つ 258 * バックグラウンドならば子プロセスの終了を待たずに 259 * main 関数に返る(プロンプト表示に戻る) 260 * 261 * 引数 : 262 * 263 * 返り値 : 264 * 265 * 注意 : 266 * 267 *--------------------------------------------------------------------------*/ 268 269void execute_command(char *args[], /* 引数の配列 */ 270 int command_status) /* コマンドの状態 */ 271{ 272 int pid; /* プロセスID */ 273 int status; /* 子プロセスの終了ステータス */ 274 275 276 /* 277 * 子プロセスを生成する 278 * 279 * 生成できたかを確認し、失敗ならばプログラムを終了する 280 */ 281 282 pid = fork (); 283 if(-1==pid){ 284 err(EXIT_FAILURE,"can not fork"); 285 exit(1); 286 } 287 288 /* 289 * 子プロセスの場合には引数として与えられたものを実行する 290 * 291 * 引数の配列は以下を仮定している 292 * ・第1引数には実行されるプログラムを示す文字列が格納されている 293 * ・引数の配列はヌルポインタで終了している 294 */ 295 if(strcmp(args[0],"cd")==0) cd_command(args); 296 else 297 if(strcmp(args[0],"pushd")==0) pushd_command(args); 298 else 299 if(strcmp(args[0],"dirs")==0) dirs_command(args); 300 else 301 if(strcmp(args[0],"popd")==0) popd_command(args); 302 else 303 if(pid==0){ 304 305 execvp(*args,args); 306 perror(*args); 307 exit(1); 308 } 309 310 /* 311 * コマンドの状態がバックグラウンドなら関数を抜ける 312 */ 313 314 if(command_status==1)return; 315 316 /* 317 * ここにくるのはコマンドの状態がフォアグラウンドの場合 318 * 319 * 親プロセスの場合に子プロセスの終了を待つ 320 */ 321 322 /******** Your Program ********/ 323 if(pid!=0) wait(&status); 324 325 return; 326} 327 328void cd_command(char *args[]){ 329 if(args[1]==NULL) chdir(getenv("HOME")); 330 else 331 if(chdir(args[1])==-1) printf("no such directory\n"); 332 return; 333} 334 335void pushd_command(char *args[]){ 336 struct stack *list; 337 struct stack *newstr; 338 list=topad->next; 339 char cwd[BUFLEN]; 340 getcwd(cwd,sizeof(cwd)); 341 342 if(list->path[0]=='\0'){ 343 strcpy(list->path,cwd); 344 list->next=NULL; 345 }else{ 346 newstr = (struct stack*)malloc(sizeof(struct stack)); 347 strcpy(newstr->path,cwd); 348 topad->next=newstr; 349 newstr->next=list; 350 } 351 return; 352} 353 354void dirs_command(char *args[]){ 355 struct stack *list; 356 list=topad->next; 357 while(list!=NULL){ 358 printf("%s\n",list->path); 359 list=list->next; 360 } 361 return; 362} 363 364void popd_command(char *args[]){ 365 struct stack *list; 366 list=topad->next; 367 chdir(list->path); 368 topad->next=list->next; 369 list=NULL; 370} 371 372/*-- END OF FILE -----------------------------------------------------------*/

###表示内容
一回pushdをした後dirsを入力すると

command : pushd command : dirs /Users//CLASSES//C/22222 /Users//CLASSES//C/22222 (パスは一部隠す) comand :

上のように全く同じ表示が二回されてしまいます
また1回pushdしてcd ..してからpushdしさいごにdirsをすると

Command : pushd Command : cd .. Command : pushd Command : dirs /Users//CLASSES//C /Users//CLASSES//C/22222 /Users//CLASSES//C /Users//CLASSES//C/22222 Command :

という表示になります
しかし下から5行目のprintf("%s\n",list->path);の\nをぬいてprintf("%s",list->path)とすると

Command : pushd Command : dirs /Users//CLASSES//C/22222Command :

となり表示数としてはうまくいきます

しかしやはり改行入れたいのでうまくいく対策はないでしょうか...

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

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

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

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

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

y_waiwai

2020/07/29 07:13

struct stack の定義がないです。 コード全体を提示しましょう
yukikona

2020/07/29 07:19

指摘ありがとうございます。全体のソースコードとして追加させていただきました
guest

回答1

0

cd,pushd,popd,dirsのコマンドのみ別処理していますが、
これは、親プロセスと子プロセスでともに実行されます。
従って、dirsコマンドで2回表示されます。
改行を入れない場合は、1回だけ実行されているように見えますが、実際は、printfが子プロセスでも実行されています。しかしながら、その場合、改行が出力されないので、バッファ内にたまっているだけで、画面に表示されないだけです。printfは、バッファが満杯か改行を検知するか、明示的にフラッシュするかした場合のみ、出力文字が画面に出力されます。

そもそも、cd,pushd,popd,dirsのコマンドは、親プロセスと子プロセスでともに実行すべきなのでしょうか。親プロセスのみ実行すれば良いように思われるのですがいかがでしょうか?

投稿2020/07/29 11:50

tatsu99

総合スコア5424

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問