0

さて、私は C を使用して Linux 用のシェルを作成しようとしています。関数 fork() と execl() を使用して、各コマンドを実行できますが、引数を読み取ろうとして立ち往生しています。

char * command;
char ** c_args = NULL;

bytes_read = getline (&command, &nbytes, stdin);

command = strtok(command, "\n ");
int arg = 0;
c_arg = strtok(NULL, "\n ");
while( c_arg != NULL ) {
    if( c_args == NULL ) {
        c_args = (char**) malloc(sizeof(char*));
    }
    else {
        c_args = (char**) realloc( c_args, sizeof(char*) * (arg + 1) );
    }
    c_args[arg] = (char*) malloc( sizeof(char)*1024 );
    strcpy( c_args[arg], c_arg );
    c_arg = strtok(NULL, "\n ");
    arg++;
}
...
pid_t pid = fork()
...
...
execl( <path>, command, c_args, NULL)
...
...

そうすれば、引数を渡そうとすると、コマンドからエラーが発生します。次に例を示します。

ls -l

私に与えます:

ls: cannot access p��: No such file or directory

問題は c_args の割り当てであることはわかっています。どうしたの?

乾杯。

4

2 に答える 2

2

execl()引数の可変リストには使用できません。execv()またはそのバリアントの 1 つ ( execve()execvp()など)を使用する必要があります。execl()コンパイル時に存在するすべての引数がわかっている場合にのみ使用できます。ほとんどの場合、一般的なシェルはそれを認識しません。例外は、次のような場合です。

execl("/bin/sh", "/bin/sh", "-c", command_line, (char *)0);

ここでは、コマンド ラインとして (他の引数なしで) 単一の文字列を実行するシェルを呼び出しています。ただし、フル シェルのキーボードで入力する内容を処理する場合、コンパイル時に入力した引数の数を知る余裕はありません。

最も単純には、次のものを使用する必要があります。

execvp(c_args[0], c_args);

0 番目の引数であるコマンド名は、 に渡すものでなければなりませんexecvp()。それが単純なファイル名 ( no ) の場合、環境変数/のディレクトリでコマンドが検索されます。$PATHコマンド名にスラッシュが含まれている場合、指定された (相対または絶対) ファイル名を探し、存在する場合はそれを実行し、存在しない場合は失敗します。他の引数はすべてヌル終了リストにある必要がありますc_args

現在、他のメモリ割り当ての問題もある可能性があります。私はコードを精査していません。ただし、引数リストの診断出力によってそれらを確認できます。

char **pargs = c_args;
while (*pargs != 0)
    puts(*pargs++);

これにより、各引数が別々の行に出力されます。null ポインターに遭遇するまで停止しないことに注意してください。引数文字列へのポインターのリストを null で終了することが重要です。

あなたのコードのこのビット:

c_args[arg] = (char*) malloc( sizeof(char)*1024 );
strcpy( c_args[arg], c_arg );

通常のケースではやり過ぎのように見え、極端なケースでは不適切なメモリ割り当てのように見えます。文字列をコピーするときは、十分な長さを割り当ててください。strtok()文字列をバラバラにするためにを使用していることがわかります — シェルの初期の化身では十分ですが、 のようなコマンド ラインを処理するようになると、が到達する前に区切り文字を踏みにじる傾向ls -l>$tmpがあることがわかりますstrtok()それは大きな責任になります。ただし、それを使用している間は、おそらくそのように引数をコピーする必要はありません。設定するだけですc_args[arg++] = result_from_strtok;。コピーする必要がある場合は、おそらくstrdup();を使用する必要があります。'\0'たとえば、末尾の に十分なスペースを割り当てることを忘れず、過剰割り当ても不足割り当てもありません。

于 2012-12-15T07:20:05.303 に答える
1

ジョナサンは素晴らしい答えを持っています。さらにいくつか追加したかっただけです。

orを使用して、シェルで直接実行することができます。それらは非常に簡単に注入できるため、通常は眉をひそめられますが、オープンシェルを作成している場合、それらを使用しても害はないと思います.popensystem

制限付きのシェル (sh のような構文を受け入れる) を使用する場合は、wordexpを調べてください。多くのことを行いますが、私の経験では、特に適度に安全なインタープリターを作成しようとしている場合は、やりすぎです (チルダ展開や変数置換などのばかげたことを行います)。

于 2012-12-15T08:17:57.363 に答える