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

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

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

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

標準入力

標準入力(stdin)は、プログラムが標準的に用いるデータ入力元。リダイレクトしない限り、プログラムを起動した端末のキーボードが標準入力になります。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

UNIX

UNIXとは、AT&Tのベル研究所で開発されたコンピューター用のマルチユーザー・マルチタスクのオペレーションシステム(OS)です。政府や教育機関や研究所で広範囲に採用されています。

リダイレクト

プログラムの入力元や出力先を通常とは別の場所に転送させることをリダイレクトと呼びます。

例外処理

例外処理(Exception handling)とは、プログラム実行中に異常が発生した場合、通常フローから外れ、例外として別の処理を行うようにデザインされたプログラミング言語構造です。

Q&A

解決済

4回答

1375閲覧

入力のリダイレクトを用いた処理

SUNMOON_14

総合スコア20

C

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

標準入力

標準入力(stdin)は、プログラムが標準的に用いるデータ入力元。リダイレクトしない限り、プログラムを起動した端末のキーボードが標準入力になります。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

UNIX

UNIXとは、AT&Tのベル研究所で開発されたコンピューター用のマルチユーザー・マルチタスクのオペレーションシステム(OS)です。政府や教育機関や研究所で広範囲に採用されています。

リダイレクト

プログラムの入力元や出力先を通常とは別の場所に転送させることをリダイレクトと呼びます。

例外処理

例外処理(Exception handling)とは、プログラム実行中に異常が発生した場合、通常フローから外れ、例外として別の処理を行うようにデザインされたプログラミング言語構造です。

0グッド

0クリップ

投稿2021/07/19 01:39

編集2021/07/19 15:42

質問

作成したプログラム以下の構文でシェルに与えることを考えます。

./a.out < test.txt

その際、"test.txt"の終端EOFを読み取ってプログラムを終了したいのですが、どうすればよいでしょうか?

現状

C

1int main(){ 2 char buf[1024]; 3 char prompt[NAMESIZE] = "Command: "; 4 for(;;){ 5 printf("%s", prompt); 6 if(fgets(buf, 1024, stdin) == NULL){ 7 exit(0); 8 } 9 (以下、入力がきちんと与えられたときの処理) 10 } 11 return 0; 12}

入力のリダイレクトに対応するため、if(scanf("%1023s", buf) == EOF){のブロックを追加したのですが、ファイルの終端でもプログラムが終了しません。

test.txtの内容

pushd
dirs
cd
ls *
alias sl ls
popd
sl -l

と書いているのですが、上から6行はきちんと読み取れていることを確認していますが、
7行目は「sl -lushd」
となる上、無限ループが終了しません。

さらに、(架空の8行目?として)dirsが読み取られています。…以下同様に無限ループが終了しない

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

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

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

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

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

jimbe

2021/07/19 03:22

fgets が NULL を返したら EOF (かエラー) だったと思いますが、それ以外になぜ判定が必要なのでしょう。
SUNMOON_14

2021/07/19 03:56

それも理解していますし、scanfが明らかにおかしいことも分かっていますが、解決策が分かりません。 標準入力をファイルの中身に変えた場合、ファイルの終わりで読み取りを終了したいのですが、どうすればよいでしょうか? EOFを検出してプログラムを終了させればよいのでしょうが、その方法が分かりません。。。
SUNMOON_14

2021/07/19 04:04 編集

for(;;){ printf("%s", prompt_str); // プロンプトを表示 if(fgets(command_buffer, BUFLEN, stdin) == NULL){ printf("\n"); continue; } (以降の処理省略:command_bufferを加工して引数化して、自作コマンドに対応) という記述です。つまり、自作シェルのコマンドプロンプトを表示し、標準入力が何もなかったら改行するという一節なのですが、ここにreturn文を入れてしまうと、リダイレクトではない標準入力でバグが出ると思います。 リダイレクトによる入力にも対応するための方法が分からないです。
jimbe

2021/07/19 08:03

> リダイレクトではない標準入力でバグが出ると思います 「出ました」では無く「出ると思います」とはどういうことでしょう。 既存のシェル(sh? csh?)の動作を参考に作られていると思いましたが、fgets のみで作った場合に、コンソール入力・ファイル入力(リダイレクト)両方で、既存のシェルの動作と同じになっているかの確認はされていないのでしょうか。
SUNMOON_14

2021/07/19 08:13

>「出ました」では無く「出ると思います」とはどういうことでしょう。 自明ではないですか? 「Command: 」という待機画面でエンターキーのみを押した場合、改行されて再度「Commands: 」を出力する本プログラムにおいて、改行後にreturn 0;と記述されているプログラムは間違いなく動作終了してまったく使い物にならないと思います。(これを「バグ」と表現しました) コンソール入力には対応していますが、リダイレクトには対応していません。 それを解決する方法が分からないため、質問しています。
SUNMOON_14

2021/07/19 08:26

コンソールで標準入力する際には、exitと打つことで動作終了するようにプログラムしています。 リダイレクトのときはユーザーがexitと打つことなく動作終了したいのですが、どうすればよいでしょうか?
jimbe

2021/07/19 08:30

すいません、作ろうとされているプログラムの仕様を確認させてください。 まず、作られているのは ```./a.out < test.txt``` にある a.out に該当するプログラムですね。 そしてそのソースコードは「現状」の所にある c のコードですね。 仕様としては、プロンプト表示後、一行入力されたら、現在(省略していますが)continue; としている部分で行の内容に合わせた処理(cd や ls等)を実行し、またプロンプトの表示に戻る、ということで合っていますか。 そして、標準入力がファイルにリダイレクトされている場合は、そのファイルの各行の処理を(ファイルを読みながら連続して)実行(=バッチ処理)して、またプロンプト表示に戻る、ということでしょうか。
SUNMOON_14

2021/07/19 08:36 編集

以下に述べる箇所を除いて、おっしゃる通りです。
jimbe

2021/07/19 08:34

上のコメントを書くのに時間が掛かってしまい exit の件が読めませんでした。さらに考え直します。
SUNMOON_14

2021/07/19 08:36

>そして、標準入力がファイルにリダイレクトされている場合は、そのファイルの各行の処理を(ファイルを読みながら連続して)実行(=バッチ処理)して、またプロンプト表示に戻る、ということでしょうか。 リダイレクトした場合はファイル内容を読み切ったのち、プロンプトに戻らず、終了したいです。
guest

回答4

0

ベストアンサー

EOFになれば、fgetsNULLを返すので、if(fgets(buf, 1024, stdin) == NULL)が成立して、
次にscanfでもすでにEOFなのでEOFが返り、return 0;が実行されるはずです。

そうなっていないのなら、実行しているプログラムと質問文に書いてあるプログラムが違うのでしょう。

なお、EOFの判定を2回するのは無意味なので、if(fgets(buf, 1024, stdin) == NULL)の判定だけで良いです。

投稿2021/07/19 15:09

otn

総合スコア85901

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

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

SUNMOON_14

2021/07/19 15:44 編集

プログラムを書き直しました。 これでもファイルの内容を無限ループで読み取ってしまいます。 実行しているプログラムと質問文に書いているプログラムは本質的に同じだと思いますが。。。
otn

2021/07/19 15:54 編集

> 実行しているプログラムと質問文に書いているプログラムは本質的に同じだと思いますが。。。 ということは違うプログラムと言うこと???? 質問文に書いたプログラムを実行もせずに質問しているのですか????
SUNMOON_14

2021/07/19 15:57

違うプログラムです。というか、600行程度あるので関係のないところは除いています。 質問文に書いているプログラムは実行しています。
SUNMOON_14

2021/07/19 16:13

test.txtを dirs cd とすればプログラムは終了できたので別のところに原因がありそうです。
otn

2021/07/19 16:18

質問文のコード(日本語の行は削除)で、質問文のtest.txtで、無限ループにはならないので、やはり質問文のコードを実行しているとは思えないですね。手順をよく見直しましょう。
SUNMOON_14

2021/07/19 16:25

ありがとうございます。 test.txtを dirs cd として、./a.out < test.txtとしてprintfで出力したとき dirsはdirs cdはcdcと出力されました。(テキストの内容を変えても毎回最後の行が変です) 何か原因は考えられますか? ――― test.txtを dirs cd history としたとき、 dirsはdirs、 cdはcd、 historyはhistorycd (なぜかもう一度読み取って)historyhistor
otn

2021/07/19 16:34

質問文のコードではそういうことは起こらないので、ちゃんと質問文のコードが実行されていることを確認しましょう。 コンパイル済みの実行ファイルを全部削除してから、質問文のコードをコンパイルすれば、間違って他のプログラムを実行することも無いでしょう。 プログラムの冒頭に printf("2021-07-20 01:35\n"); とか入れて、確かにこのコードかどうか確認するとか。 もしくは、入力ファイルの中身がおかしいとか。 cat -v test.txt してちゃんと表示されますか?
guest

0

試し々々で以下のようなコードになりました。
リダイレクトされているかを判断している fromFile 関数の中の _S_ISREG() につきましては、当初 FIFO のつもりでしたが、こちらの環境 Windows/MinGW では判断が付かなかったので、実際にリダイレクト有り/無しで試して _S_IFMT の中で値が異なっていた REG ビットを使いました。
fprintf(stderr,~ でイロイロ表示していますが、どれも確認の為なので全て消して大丈夫…だと思います。

c

1#include <stdio.h> 2#include <string.h> 3#include <sys/stat.h> 4 5//コマンド実行 6void run(char *commandline) { 7 fprintf(stderr, "[run - '%s']\n", commandline); 8} 9//標準入力がリダイレクトされているかを返す. 10int fromFile() { 11 struct stat sb; 12 if(fstat(STDIN_FILENO, &sb) != -1) { 13 fprintf(stderr, "[sb/st_mode=0x%04x]\n", sb.st_mode); 14 return _S_ISREG(sb.st_mode); //レギュラー? 15 } 16 return 0; //fstatのエラーは無視 17} 18 19int main() { 20 char commandline[256]; 21 int isBatch = fromFile(); 22 fprintf(stderr, "[isBatch=%s]\n", isBatch?"yes":"no"); 23 while(1) { 24 if(!isBatch) printf("prompt:"); 25 memset(commandline, 0, sizeof(commandline)); 26 if(fgets(commandline, 256, stdin) == NULL) break; //EOF 27 //改行コード削除 28 if(commandline[strlen(commandline)-1] == '\n') commandline[strlen(commandline)-1]='\0'; 29 //終了 30 if(!strcmp(commandline,"exit")) break; 31 //コマンド実行 32 run(commandline); 33 } 34 fprintf(stderr, "[exit]\n"); 35}

投稿2021/07/19 09:48

jimbe

総合スコア13209

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

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

0

fgetsのみが入力をとって、NULLを返したらEOF。
つまりそのコードで、scanfでなく、sscanfをつかうようにすればいいのでは。

投稿2021/07/19 02:42

matukeso

総合スコア1681

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

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

0

if(fgets(buf, 1024, stdin) == NULL){

if(scanf("%1023s", buf) == EOF){

1行読み捨てて、それからscanfしてるけど、これってなにしてるんでしょう
#ああ、違うか。全行読み捨てたあとscanfしてるのか。よけい意味不明

投稿2021/07/19 02:02

編集2021/07/19 02:05
y_waiwai

総合スコア88042

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

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

SUNMOON_14

2021/07/19 02:28 編集

添付したプログラムでは省略していますが、fgetsで得たbufは、後に加工します。 scanfに関しては適当です。 fgetsで標準入力を取得する仕様は変えたくないです。(他のプログラム600行に影響が出るため) 入力のリダイレクトでファイルの中身が入力として与えられた場合、ファイルの終わりでプログラムを終える方法が分かりません。 教えていただけないでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問