あなたは子供のclose(fd[0]);
後にすべきです。dup2()
のように絶対パスまたは相対パスを指定すると、 ;"/bin/sh"
を使用しても意味がありません。execlp()
裸のファイル名 (プログラム名) の PATH ベースの検索のみを行います。への呼び出しでのキャストはgetline()
不要です。このようなキャストは可能な限り避けてください。失敗した場合に備えて、少なくともexit(1);
afterを含める必要があります。execlp()
診断メッセージも良い考えです。close(fd[1]);
子に EOF を示すには、親のループの後にする必要があります。(一度だけ、 からのエラー戻りを検出しなくても問題ありませんmalloc()
。ポインタが NULL を保持しているポインタのアドレスをgetline()
機能し、メモリ自体を割り当てようとします。もちろん、メイン プログラムがメモリの割り当てに失敗した場合、メイン プログラムgetline()
もメモリの割り当てに失敗する可能性が高くなります。)
これらの変化は次のことにつながります。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd[2];
pid_t child;
if (pipe(fd) != 0)
perror("pipe");
else if ((child = fork()) < 0)
perror("fork");
else if (child == 0)
{
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execl("/bin/sh", "sh", NULL);
perror("oops");
exit(1);
}
else
{
close(fd[0]);
size_t nbytes = 100;
int bytes = 0;
char *line = (char*)malloc(nbytes+1);
while ((bytes = getline(&line, &nbytes, stdin)) != -1)
{
write(fd[1], line, bytes);
}
close(fd[1]);
}
return(0);
}
これは、厳しいコンパイル フラグの下で問題なくコンパイルされます。
gcc -O3 -g -std=c99 -Wall -Wextra xf.c -o xf
(Mac OS X 10.7.3 上で) 上記のコード (オプションsh
なしで起動) を実行すると、問題なく正常に動作します。-i
コマンドを入力すると、シェルがそれらを実行します。「exit」と入力するとシェルは終了しますが、あなたが作成したプログラム (私が呼び出したものxf
) は、新しいコマンドを入力するまで終了しません。次に、現在リーダーのないパイプに書き込むときに、SIGPIPE シグナルのために終了します。標準入力が端末ではない (パイプである) ため、このシェルからのプロンプトはありません。
オプションを指定してサブシェルを実行すると、-i
どのシェルが端末を担当するかについて、ジョブ制御シェル間で争いが発生するようです。実行すると、次のようになります。
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
$ ./xf
sh-3.2$
[1]+ Stopped(SIGTTIN) ./xf
$
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
503 22000 21908 0 9:36PM ttys001 0:00.00 ./xf
503 22001 22000 0 9:36PM ttys001 0:00.00 sh -i
$ ls
awk.data osfile-keep.c pthread-2.c send.c xf
const-stuff.c perl.data pthread-3.c so.8854855.sql xf.c
fifocircle.c piped-merge-sort.c quine.c strandsort.c xf.dSYM
madump.c powa.c recv.c unwrap.c xxx.sql
makefile pthread-1.c regress.c vap.c yyy.sql
$ jobs
[1]+ Stopped(SIGTTIN) ./xf
$ fg %1
./xf
exit
$
(イニシャル-sh
はターミナル ウィンドウのログイン シェルです。その中で、実行してサブシェルを取得し、プロンプトを区別するようにsh
設定しました。)PS1='$ '
AFAICT、sh-3.2$
プロンプトはsh -i
シェルから来ます。親シェルは入力を読み取っているようで、xf
プログラムをバックグラウンドにダンプしましたが、これはあまり文明化されていません。出力にはコマンドが表示されないため、面倒ですps -f
。1回の実行でコマンドをリストに表示するps
ことができましたが、それは元のシェルの子であり、実行によるものではありませんでした。フォアグラウンドに移動すると、すぐに終了します (おそらく、EOF を示す標準入力から 0 バイトを読み取るため、-1 が返され、すべてがシャットダウンします。シェルが許可する代わりにコマンドを受け取ったためls
ps
sh -i
xf
xf
getline()
exit
sh -i
sh
xf
端末を制御できます。それはかなり厄介です。どうしてこうなったのかはわかりませんが、あってはならないことだと思います。