[解決済み] C言語でシェルを実装し、入出力のリダイレクションを処理する手助けが必要です。
質問内容
第2ラウンド
いくつかの回答を読んで、私の修正したコードは。
int pid = fork();
if (pid == -1) {
perror("fork");
} else if (pid == 0) {
if (in) { //if '<' char was found in string inputted by user
int fd0 = open(input, O_RDONLY, 0);
dup2(fd0, STDIN_FILENO);
close(fd0);
in = 0;
}
if (out) { //if '>' was found in string inputted by user
int fd1 = creat(output, 0644);
dup2(fd1, STDOUT_FILENO);
close(fd1);
out = 0;
}
execvp(res[0], res);
perror("execvp");
_exit(1);
} else {
waitpid(pid, 0, 0);
free(res);
}
動作はするのですが、標準出力が再接続されていないとか、そのようなことがあるようです。以下は実行です。
SHELL$ cat > file
hello, world
this is a test
SHELL$ cat < file //no output
SHELL$ ls //no output
'<' と '>' はどちらも動作しますが、実行後は何も出力されません。
第1ラウンド
C言語で比較的簡単なシェルを作っているのですが、入力(<)と出力(>)のリダイレクトの実装に困っています。以下のコードの問題点を見つけるのを手伝ってください。
int fd;
int pid = fork();
int current_out;
if (in) { //if '<' char was found in string inputted by user
fd = open(input, O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
in = 0;
current_out = dup(0);
}
if (out) { //if '>' was found in string inputted by user
fd = creat(output, 0644);
dup2(fd, STDOUT_FILENO);
out = 0;
current_out = dup(1);
}
if (pid == -1) {
perror("fork");
} else if (pid == 0) {
execvp(res[0], res);
perror("execvp");
_exit(1);
} else {
waitpid(pid, 0, 0);
dup2(current_out, 1);
free(res);
}
いろいろと試行錯誤しているので、不要なものが入っているかもしれません。何が間違っているのかよくわかりません。
解決方法は?
リダイレクトの後、ファイルディスクリプタを開きすぎています。 2つの段落を分解してみましょう。
if (in) { //if '<' char was found in string inputted by user
fd = open(input, O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
in = 0;
current_in = dup(0); // Fix for symmetry with second paragraph
}
if (out) { //if '>' was found in string inputted by user
fd = creat(output, 0644);
dup2(fd, STDOUT_FILENO);
out = 0;
current_out = dup(1);
}
エラーを無視しているのはご愛敬です。 ただし、システムコールのエラーチェックは必要です。
最初の段落では、ファイルを開き、ファイル記述子(3かもしれない)を変数
fd
. 次に、そのファイルディスクリプタを標準入力に複製します (
STDIN_FILENO
). ただし、ファイルディスクリプタ3はまだ開いていることに注意。 次に
dup(0)
(一貫性を保つため、本来は
STDIN_FILENO
を取得し、別のファイル記述子(おそらく4)を取得します。 つまり、ファイル記述子 0、3、4 が同じファイルを指していることになります(実際、同じオープン ファイル記述子があります - オープン ファイル記述子はオープン ファイル記述子とは異なることに注意してください)。 もし、あなたが
current_in
(親)シェルの標準入力を保持するため、そのようにしなければなりません。
dup()
を実行する前に
dup2()
で出力を上書きします。 しかし、親シェルのファイルディスクリプタを変更しない方が良いでしょう。ファイルディスクリプタを再複製するよりもオーバーヘッドが少なくて済みます。
次に、2番目の段落の処理をほぼ繰り返し、まずファイル記述子3が開かれている唯一の記録を
fd = creat(...)
を呼び出しますが、新しい記述子、おそらく5を取得し、それを標準出力に複製します。 次に
dup(1)
その結果、別のファイル記述子、おそらく6が生成されます。
つまり、メインシェルのstdinとstdoutがファイルにリダイレクトされている(そして、それを元の値に戻す方法がない)状態になっているわけです。 したがって、最初の問題は、リダイレクトを行う前に
fork()
の後に行う必要があります。
fork()
- ただし、プロセス間のパイプを作成する場合は、フォークする前にパイプを作成する必要があります。
2つ目の問題は、大量のファイルディスクリプタをクローズする必要があり、そのうちの1つはもはや参照先がないことです。
そこで、必要になるかもしれないのが
if ((pid = fork()) < 0)
...error...
else if (pid == 0)
{
/* Be childish */
if (in)
{
int fd0 = open(input, O_RDONLY);
dup2(fd0, STDIN_FILENO);
close(fd0);
}
if (out)
{
int fd1 = creat(output , 0644) ;
dup2(fd1, STDOUT_FILENO);
close(fd1);
}
...now the child has stdin coming from the input file,
...stdout going to the output file, and no extra files open.
...it is safe to execute the command to be executed.
execve(cmd[0], cmd, env); // Or your preferred alternative
fprintf(stderr, "Failed to exec %s\n", cmd[0]);
exit(1);
}
else
{
/* Be parental */
...wait for child to die, etc...
}
この作業を行う前に、シェルの標準 I/O チャンネルをすでにフラッシュしていることを確認する必要があります。
fflush(0)
そうすれば、もしフォークされた子プロセスが問題のために標準エラーに書き込んでも、余計な重複出力が発生しません。
また、様々な
open()
の呼び出しは、エラーチェックが必要です。
関連
-
[解決済み】単項演算子「*」の型が無効(「int」がある)C言語でのエラー
-
[解決済み】 `S_ISREG()` とは何ですか、そして何をするのですか?
-
[解決済み] 複数のデバイスを接続しているときにADB Shellを使用するには?error: more than one device and emulator "で失敗します。
-
[解決済み] LinuxのシェルスクリプトでYes/No/Cancelの入力を促すにはどうしたらいいですか?
-
[解決済み] Bashシェルスクリプトの入力引数の存在確認
-
[解決済み] 出力をファイルや標準出力にリダイレクトする方法
-
[解決済み] シェルスクリプトでブール変数を宣言して使用するにはどうすればよいですか?
-
[解決済み] シェルコマンドの実行と出力のキャプチャ
-
[解決済み] シェル変数の周りに中括弧が必要なのはどんなとき?
-
[解決済み] Bashでパイプ出力と終了ステータスをキャプチャする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] c - 初期化がキャストなしでポインタから整数を作る、さらに2つのコンパイラーエラー
-
[解決済み】変数の警告が設定されているが使用されていない
-
[解決済み】組み込み関数「malloc」の暗黙の宣言の非互換性
-
[解決済み] strtokのセグメンテーションフォールト
-
[解決済み】"Expected expression before ' { ' token"(トークンの前に期待される式)。
-
[解決済み】エラー:'for'ループの初期宣言はC99モードでしかできない【重複
-
[解決済み】メモリー・クロバリング・エラー
-
[解決済み] C: エラー: ';'トークンの前に ')' があると予想される
-
[解決済み】インクリメントオペランドとして lvalue が必要です。
-
[解決済み】c - 警告:関数 'printf'の暗黙の宣言