linuxでshellコマンドをc言語で呼び出す方法
Cプログラム呼び出しshellスクリプトには、system()、popen()、execシリーズ数call_の3つのメソッドがあります.exec1.c ,
System()は自分でプロセスを生成する必要はありません.それはすでにカプセル化されています.直接自分のコマンドexecに参加するには、自分のforkプロセスが必要です.それからexecの自分のコマンドが必要です.
popen()はシステムよりもコストが小さいコマンドを実行することもできます
方法一、システム()の使用、私は直接コードに行きましょう
私は/home/book/shellにtestを新規作成しました.shファイルは以下の通りです.
test.cファイルは以下の通りです.
次のコマンドを実行してコンパイルします.
テストコマンド:
結果は次のとおりです.
方法2:popen()はfork()を呼び出してサブプロシージャを生成し、サブプロシージャから/bin/sh-cを呼び出してパラメータcommandの命令を履行する.パラメータtypeは、読み取りを表す「r」を適用し、書き込みを表す「w」を適用します.このtype値に従うと、popen()はサブプロシージャにパイプを接続する標準出力デバイスまたは標準入力デバイスを確立し、ファイルポインタを返します.その後、プロシージャは、このファイルポインタを使用して、サブプロシージャの出力デバイスを読み取るか、サブプロシージャの標準入力デバイスに書き込むことができる.また、fclose()以外のすべてのアプリケーションファイルポインタ(FILE*)操作の関数も適用できます.戻り値:成功したらファイルポインタを返し、そうでなければNULLを返し、エラーの原因はerrnoに保存されます.注意:SUID/SGID権限を持つプログラムを作成する時、できるだけpopen()を適用しないでください、popen()は環境変数を継承して、環境変数を通じてシステムの安全な問題をもたらすかもしれません
他は変えなくてもtestを直接修正します.cファイル:
方法3:exec関数クラスタ(よくわかりませんが、copyは他の人のもので、検証もしていません.習慣方法1)
execは1つの関数ではありませんが、実際には関数のセットの総称であり、次の6つの関数が含まれています.
この6つの関数の名前が異なり、彼らが受け入れるためのパラメータも異なることがわかる.
実は彼らの机能はすべて悪くなくて、异なるパラメータを受け入れるために使うため异なる名前でそれらを区别して、结局c言语は関数のリロードの机能がありません..
しかし、実際には命名には規則があります.
exec[l or v][p][e]
exec関数のパラメータは、3つの部分、実行ファイル部分、コマンドパラメータ部分、環境変数部分に分けることができる.
例えばls-l/home/gatemanのコマンドを1つ実行します
実行ファイルセクションは「/usr/bin/ls」です.
命令参加部分は「ls」、「-l」、「/home/gateman」で、NULLはlsで始まる1つのスペースごとに2つの部分に分けなければならないし、NULLで終わるだろう.
環境変数部分、これは1つの配列で、最後の要素はNULL、例えばchar*env[]={"PATH=/home/gateman"、"USER=lei"、"STATUS=testing"、"NULL};
では、命名ルールについてお話しします.
eその後、パラメータは環境変数部分を持たなければならず、環境がゼロになる部分パラメータはexec関数を実行する間の環境変数となり、比較的少ない
l以降、コマンドパラメータ部分は「,」で区切らなければならず、最後の1つのコマンドパラメータはNULLでなければならない
v以降、コマンドパラメータ部分は、NULLで終わる文字列ポインタ配列のヘッダポインタである必要がある.例えばchar*pstrは1文字列のポインタであり、char*pstr[]は配列であり、それぞれの文字列を指す.
pその後、実行ファイル部分にパスを持たなくてもいいです.exec関数は$PATHで探します.
もう1つの注意点は、exec関数が実行するプロセスに取って代わる、すなわち、exec関数が実行に成功すると、それは戻らず、プロセスが終了することである.しかし、exec関数の実行に失敗すると、失敗した情報が返され、プロセスは後続のコードを実行し続けます.
通常execはfork()関数のサブプロセス部分に置かれ、サブプロセスの代わりに実行されます.実行に成功するとサブプログラムは消えますが、実行に失敗するとexit()関数でサブプロセスを終了させなければなりません.
以下に例を示します.
2.1 execv関数
[cpp] view plain copy
int childpid;
int i;
if (fork() == 0){
//child process
char * execv_str[] = {"echo", "executed by execv",NULL};
if (execv("/usr/bin/echo",execv_str) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execv done");
}
文字列ポインタ配列の定義と割り当てに注意
2.2 execvp関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};
if (execvp("echo",execvp_str) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execvp done");
}
2.3 execve関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * execve_str[] = {"env",NULL};
char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};
if (execve("/usr/bin/env",execve_str,env) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execve done");
}
2.4 execl関数
[cpp] view plain copy
if (fork() == 0){
//child process
if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execv done");
}
2.5 execlp関数
[cpp] view plain copy
if (fork() == 0){
//child process
if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execlp done");
}
2.6 execle関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};
if (execle("/usr/bin/env","env",NULL,env) <0){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execle done");
}
System()は自分でプロセスを生成する必要はありません.それはすでにカプセル化されています.直接自分のコマンドexecに参加するには、自分のforkプロセスが必要です.それからexecの自分のコマンドが必要です.
popen()はシステムよりもコストが小さいコマンドを実行することもできます
方法一、システム()の使用、私は直接コードに行きましょう
int system(const char *command);
私は/home/book/shellにtestを新規作成しました.shファイルは以下の通りです.
#!bin/bash
echo $HOME
echo "the is test!"
test.cファイルは以下の通りです.
#include
int main()
{
system("bash /home/book/shell/test.sh"); /* chmod +x test.sh , bash */
return 0;
}
次のコマンドを実行してコンパイルします.
gcc test.c -o test
テストコマンド:
./test
結果は次のとおりです.
/root
the is test!
方法2:popen()はfork()を呼び出してサブプロシージャを生成し、サブプロシージャから/bin/sh-cを呼び出してパラメータcommandの命令を履行する.パラメータtypeは、読み取りを表す「r」を適用し、書き込みを表す「w」を適用します.このtype値に従うと、popen()はサブプロシージャにパイプを接続する標準出力デバイスまたは標準入力デバイスを確立し、ファイルポインタを返します.その後、プロシージャは、このファイルポインタを使用して、サブプロシージャの出力デバイスを読み取るか、サブプロシージャの標準入力デバイスに書き込むことができる.また、fclose()以外のすべてのアプリケーションファイルポインタ(FILE*)操作の関数も適用できます.戻り値:成功したらファイルポインタを返し、そうでなければNULLを返し、エラーの原因はerrnoに保存されます.注意:SUID/SGID権限を持つプログラムを作成する時、できるだけpopen()を適用しないでください、popen()は環境変数を継承して、環境変数を通じてシステムの安全な問題をもたらすかもしれません
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
他は変えなくてもtestを直接修正します.cファイル:
#include
int main()
{
char buffer[80];
FILE *fp=popen("bash /home/book/shell/test.sh","r");
fgets(buffer,sizeof(buffer),fp);
printf("%s",buffer);
pclose(fp);
return 0;
}
方法3:exec関数クラスタ(よくわかりませんが、copyは他の人のもので、検証もしていません.習慣方法1)
execは1つの関数ではありませんが、実際には関数のセットの総称であり、次の6つの関数が含まれています.
#include
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[];
この6つの関数の名前が異なり、彼らが受け入れるためのパラメータも異なることがわかる.
実は彼らの机能はすべて悪くなくて、异なるパラメータを受け入れるために使うため异なる名前でそれらを区别して、结局c言语は関数のリロードの机能がありません..
しかし、実際には命名には規則があります.
exec[l or v][p][e]
exec関数のパラメータは、3つの部分、実行ファイル部分、コマンドパラメータ部分、環境変数部分に分けることができる.
例えばls-l/home/gatemanのコマンドを1つ実行します
実行ファイルセクションは「/usr/bin/ls」です.
命令参加部分は「ls」、「-l」、「/home/gateman」で、NULLはlsで始まる1つのスペースごとに2つの部分に分けなければならないし、NULLで終わるだろう.
環境変数部分、これは1つの配列で、最後の要素はNULL、例えばchar*env[]={"PATH=/home/gateman"、"USER=lei"、"STATUS=testing"、"NULL};
では、命名ルールについてお話しします.
eその後、パラメータは環境変数部分を持たなければならず、環境がゼロになる部分パラメータはexec関数を実行する間の環境変数となり、比較的少ない
l以降、コマンドパラメータ部分は「,」で区切らなければならず、最後の1つのコマンドパラメータはNULLでなければならない
v以降、コマンドパラメータ部分は、NULLで終わる文字列ポインタ配列のヘッダポインタである必要がある.例えばchar*pstrは1文字列のポインタであり、char*pstr[]は配列であり、それぞれの文字列を指す.
pその後、実行ファイル部分にパスを持たなくてもいいです.exec関数は$PATHで探します.
もう1つの注意点は、exec関数が実行するプロセスに取って代わる、すなわち、exec関数が実行に成功すると、それは戻らず、プロセスが終了することである.しかし、exec関数の実行に失敗すると、失敗した情報が返され、プロセスは後続のコードを実行し続けます.
通常execはfork()関数のサブプロセス部分に置かれ、サブプロセスの代わりに実行されます.実行に成功するとサブプログラムは消えますが、実行に失敗するとexit()関数でサブプロセスを終了させなければなりません.
以下に例を示します.
2.1 execv関数
[cpp] view plain copy
int childpid;
int i;
if (fork() == 0){
//child process
char * execv_str[] = {"echo", "executed by execv",NULL};
if (execv("/usr/bin/echo",execv_str) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execv done");
}
文字列ポインタ配列の定義と割り当てに注意
2.2 execvp関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};
if (execvp("echo",execvp_str) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execvp done");
}
2.3 execve関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * execve_str[] = {"env",NULL};
char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};
if (execve("/usr/bin/env",execve_str,env) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execve done");
}
2.4 execl関数
[cpp] view plain copy
if (fork() == 0){
//child process
if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execv done");
}
2.5 execlp関数
[cpp] view plain copy
if (fork() == 0){
//child process
if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execlp done");
}
2.6 execle関数
[cpp] view plain copy
if (fork() == 0){
//child process
char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};
if (execle("/usr/bin/env","env",NULL,env) <0){
perror("error on exec");
exit(0);
}
}else{
//parent process
wait(&childpid);
printf("execle done");
}