ncurses を使用してウィンドウでコマンドライン出力をキャプチャする方法は?
「ls」のようなコマンドを実行しているとします。その出力を、ncurses で設計された特定のウィンドウに出力したいとします。ncurses は初めてです。助けてください。よろしくお願いします。
ncurses を使用してウィンドウでコマンドライン出力をキャプチャする方法は?
「ls」のようなコマンドを実行しているとします。その出力を、ncurses で設計された特定のウィンドウに出力したいとします。ncurses は初めてです。助けてください。よろしくお願いします。
より洗練された解決策は、プログラム内にリダイレクトを実装することです。dup() および dup2() システム コールを調べます (dup(2) マンページを参照)。したがって、あなたがしたいことは次のとおりです(これは本質的に、system()によって呼び出されたシェルが最終的に行うことです):
コードスニペット:
char *tmpname;
int tmpfile;
pid_t pid;
int r;
tmpname = strdup("/tmp/ls_out_XXXXXX");
assert(tmpname);
tmpfile = mkstemp(tmpname);
assert(tmpfile >= 0);
pid = fork();
if (pid == 0) { // child process
r = dup2(STDOUT_FILENO, tmpfile);
assert(r == STDOUT_FILENO);
execl("/bin/ls", "ls", NULL);
assert(0);
} else if (pid > 0) { // parent
waitpid(pid, &r, 0);
/* you can mmap(2) tmpfile here, and read from it like it was a memory buffer, or
* read and write as normal, mmap() is nicer--the kernel handles the buffering
* and other memory management hassles.
*/
} else {
/* fork() failed, bail violently for this example, you can handle differently as
* appropriately.
*/
assert(0);
}
// tmpfile is the file descriptor for the ls output.
unlink(tmpname); // file stays around until close(2) for this process only
よりうるさいプログラム (入出力用の端末があることを気にするプログラム) については、pty(7) マンページを参照して、疑似 tty を調べる必要があります。(または 'pty' をグーグルで検索します。) これは、ls に複数列のきれいな印刷を実行させたい場合に必要です (たとえば、ls はファイルに出力していることを検出し、1 つのファイル名を 1 行に書き込みます。ls に実行させたい場合また、fork() の後で $LINES および $COLUMNS 環境変数を設定して、ls をウィンドウ サイズに合わせてきれいに出力できるようにする必要があります。重要な変更は、tmpfile = mkstemp(...); 行を削除し、その行とその周囲のロジックを pty を開くロジックに置き換え、dup2() 呼び出しを拡張して stdin と stderr も処理するようにすることです。 、pty ファイルハンドルからそれらを dup2()ing)。
ユーザーがウィンドウ内で任意のプログラムを実行できる場合は、ncurses プログラムに注意する必要があります。ncurses は move() および printw()/addch()/addstr() コマンドを適切なコンソール コードに変換します。 ncurses プログラムの出力を印刷すると、プログラムの出力が中断され、ウィンドウの場所が無視されます。GNU screen は、これを処理する方法を調べる良い例です。ncurses コードをキャッチするために VT100 端末エミュレータを実装し、独自の termcap/terminfo エントリを持つ独自の「screen」端末を実装しています。Screen のサブプログラムは疑似端末で実行されます。(xterm や他の端末エミュレーターも同様のトリックを実行します。)
最後の注意: 上記のコードはコンパイルしていません。小さなタイプミスがあるかもしれませんが、おおむね正しいはずです。mmap() を使用する場合は、必ず munmap() を使用してください。また、ls の出力が完了したら、close(tmpfile) を実行する必要があります。unlink() は、コードのずっと前、または close() 呼び出しの直前に実行できる可能性があります--遊んでいる出力を人々に見てもらいたいかどうかによって異なります-私は通常、 mkstemp の直後に unlink() を配置します() 呼び出し - これにより、tmp ディレクトリがディスクでバックアップされている場合に、カーネルがファイルをディスクに書き戻さなくなります (tmpfs のおかげで、これはますます一般的ではありません)。また、メモリ リークを防ぐために、unlink() の後に free(tmpname) を実行する必要があります。tmpname は mkstemp() によって変更されるため、strdup() が必要です。
私が考えることができる 1 つのことは、system() を使用してコマンドを実行し、その出力を一時ファイルにリダイレクトすることです。
system("ls > temp");
次に、ファイル temp を開き、その内容を読み取り、ウィンドウに表示します。
エレガントなソリューションではありませんが、機能します。
Norman Matloff は、Unix Curses Library の紹介(5 ページ) で次のように示しています。
// runs "ps ax" and stores the output in cmdoutlines
runpsax()
{ FILE* p; char ln[MAXCOL]; int row,tmp;
p = popen("ps ax","r"); // open Unix pipe (enables one program to read
// output of another as if it were a file)
for (row = 0; row < MAXROW; row++) {
tmp = fgets(ln,MAXCOL,p); // read one line from the pipe
if (tmp == NULL) break; // if end of pipe, break
// don’t want stored line to exceed width of screen, which the
// curses library provides to us in the variable COLS, so truncate
// to at most COLS characters
strncpy(cmdoutlines[row],ln,COLS);
// remove EOL character
cmdoutlines[row][MAXCOL-1] = 0;
}
ncmdlines = row;
close(p); // close pipe
}
...
mvaddstr(...)
次に、ncurses を介して配列から行を出力するように呼び出します。