[shell]C语言调用shell脚本接口
1) system(shell命令或shell腳本路徑);
? ? 執(zhí)行過程:system()會調用fork()產(chǎn)生子進程,由子進程來調用/bin/sh -c string來執(zhí)行參數(shù)string字符串所代表的的命令,此命令執(zhí)行完后隨即返回原調用的進程。在調用system()期間SIGCHLD信號會被暫時擱置,SIGINT和SIGQUIT信號會被忽略。
? ? 返回值:如果system()在調用/bin/sh時失敗則返回127,其他失敗原因返回-1.若參數(shù)string為空指針(NULL),則返回非零值。如果system()調用成功則最后會返回執(zhí)行shell命令后的返回值,但是此返回值也有可能為system()調用/bin/sh失敗所返回的127,因此最好能在檢查errno來確認執(zhí)行成功。
? ? ?注意: 在編寫具有SUID/SGID權限的程序時,盡量避免使用system(),system()會繼承環(huán)境變量,通過環(huán)境變量可能會造成系統(tǒng)安全的問題。
例子:在~/myprogram/目錄下有shell腳本test.sh,內容為:
#!bin/bash
#test.sh
echo $HOME
在該目錄下新建一個c文件systemtest.c,內容為:
#include /*This program is used to test function system*/ void main() {int rv = system("~/myprogram/test.sh");if (WIFEXITED(rv)) {printf("subprocess exited, exit code: %d\n", WEXITSTATUS(rv));if (0 == WEXITSTATUS(rv)) {//if command returning 0 means succeedprintf("command succeed");} else {if (127 == WEXITSTATUS(rv)) {printf("command not found\n");return WEXITSTATUS(rv);} else {printf("command failed: %d\n", strerror(WEXITSTATUS(rv)));return WEXITSTATUS(rv);}}} else {printf("subprocess exit failed");return -1;} }? 執(zhí)行結果如下:
xiakeyou@ubuntu:~/myprogram$ gcc systemtest.c -o systemtest
xiakeyou@ubuntu:~/myprogram$ ./systemtest
/home/d/e/xiakeyou
xiakeyou@ubuntu:~/myprogram$
2) popen(char *command, char *type)
? ? 執(zhí)行過程: popen()會調用fork()產(chǎn)生子進程,然后從子進程中調用/bin/sh -c來執(zhí)行參數(shù)command的指令。參數(shù)type可使用"r"代表讀取,"w"代表寫入。依照此type值,popen()會建立管道連到子進程的標準輸出設備或標準輸入設備,然后返回一個文件指針。隨后進程便可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標準輸入設備中。此外,所有使用文件指針(FILE *)操作的函數(shù)也都可以使用,除了fclose()以外。
? ? 返回值: 若成功則返回文件指針,否則返回NULL,錯誤愿意存于errno中。
? ? 另外可以使用pclose捕捉該條命令有沒有正確被執(zhí)行,只是pclose不能在popen后面太快不執(zhí)行,否則可能pipe error。
? However,pclose(3) returns the exit value of the process that you were running. Non-zero exit processes are typical of programs to signal error conditions on exit, and some programs are even nice enough to signal to you what the error was based on the exit value.?
? ? ?注意: 在編寫具SUID/SGID權限的程序時請盡量避免使用popen(),popen()會繼承環(huán)境變量,通過環(huán)境變量可能會造成系統(tǒng)安全的問題。
例子:C程序popentest.c內容如下:
#include main() {FILE *fp;char buffer[80];fp = popen("~/myprogram/test.sh", "r");fgets(buffer, sizeof(buffer), fp);printf("%s", buffer);pclose(fp); }執(zhí)行結果如下:
xiakeyou@ubuntu:~/myprogram$ vim popentest.c xiakeyou@ubuntu:~/myprogram$ gcc popentest.c -o popentest xiakeyou@ubuntu:~/myprogram$ ./popentest /home/d/e/xiakeyou xiakeyou@ubuntu:~/myprogram$popen函數(shù)執(zhí)行命令后,返回一個指向該命令輸出的文件句柄,接下來就可以用fgets等文件操作函數(shù)去讀取輸出結果。
#include?
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
type的參數(shù)只能是"r"或"w"
例如:
#include #include int main(int argc, char *argv[]){FILE *fstream = NULL;char buff[1024];memset(buff, 0, sizeof(buff));if (NULL == (fstream=popen("ls -l", "r"))) {fprintf(stderr, "execute command failed: %s", strerror(errno));return -1;}if (NULL != fgets(buff, sizeof(buff), fstream)) {printf("%s", buff);} else {pclose(fstream);return -1;}pclose(fstream);return 0; }3) linux exec的用法
說是exec系統(tǒng)調用,實際上在linux中,并不存在一個exec()的函數(shù)形式,exec指的是一組函數(shù),一共有6個,分別是:
#include
extern char **environ;
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[]);
其中只有execve是真正意義上的系統(tǒng)調用,其他都是在此基礎上經(jīng)過包裝的庫函數(shù)。
exec函數(shù)族的作用是根據(jù)指定的文件名找到可執(zhí)行文件,并用它來取代調用進程的內容,換句話說,就是在調用進程內部執(zhí)行一個可執(zhí)行文件。這里的可執(zhí)行文件既可以是二進制文件,也可以是任何Linux下可執(zhí)行的腳本文件。
與一般情況不同,exec函數(shù)族的函數(shù)執(zhí)行成功后不會返回,因為調用進程的實體,包括代碼段,數(shù)據(jù)段和堆棧等都已經(jīng)被新的內容取代,只留下進程ID等一些表面上的信息仍保持原樣,頗有些神似"三十六計"中的“金蟬脫殼”。看上去還是舊的軀殼,卻已經(jīng)注入了新的靈魂。只有調用失敗了,它們才會返回一個-1,從原程序的調用點接著往下執(zhí)行。
現(xiàn)在我們應該明白了,Linux下是如何執(zhí)行新程序的,每當有進程認為自己不能為系統(tǒng)和用戶做出任何貢獻了,他就可以發(fā)揮最后一點余熱,調用任何一個exec,讓自己以新的面貌重生;或者,更普遍的情況是,如果一個進程想執(zhí)行另一個程序,他就可以fork出一個新進程,然后調用任何一個exec,這樣看起來就好像通過執(zhí)行應用程序而產(chǎn)生了一個新進程一樣。
事實上第二種情況被應用的如此普遍,以至于Linux專門為其做了優(yōu)化,我們已經(jīng)知道,fork會將調用級才能拿的所有內容原封不動的拷貝到新產(chǎn)生的子進程中去,這些拷貝的動作很消耗時間,而如果fork完之后我們馬上就調用exec,這些辛辛苦苦拷貝來的東西又會被立刻抹掉,這看起來非常不劃算,于是人們設計了一種"寫時拷貝(copy-on-write)"技術,使得fork結束后并不立刻復制父進程的內容,而是到了真正實用的時候才復制,這樣如果下一條語句是exec,它就不會白白作無用功了,也就提高了效率。
返回值
如果執(zhí)行成功則函數(shù)不會返回,執(zhí)行失敗則直接返回-1,失敗原因存于errno中。
大家在平時的編程中,如果用到了exec函數(shù)族,一定記得要加錯誤判斷語句。因為與其他系統(tǒng)調用比起來,exec很容易受傷,被執(zhí)行文件的位置,權限等很多因素都能導致該調用的失敗。最常見的錯誤是:
- 找不到文件或路徑,此時errno被設置為ENOENT;
- 數(shù)組argv和envp忘記用NULL結束,此時errno被設置為EFAULT;
- 沒有對要執(zhí)行文件的運行權限,此時errno被設置為EACCES。
函數(shù)族的意義
- ?l 表示以參數(shù)列表的形式調用
- ?v 表示以參數(shù)數(shù)組的方式調用
- ?e 表示可傳遞環(huán)境變量
- ?p 表示PATH中搜索執(zhí)行的文件,如果給出的不是絕對路徑就會去PATH搜索相應名字的文件,如PATH沒有設置,則會默認在/bin,/usr/bin下搜索。
另:調用時參數(shù)必須以NULL結束。原進程打開的文件描述符是不會在exec中關閉的,除非用fcntl設置它們的“執(zhí)行時關閉標志(close on exec)”而原進程打開的目錄流都將在新進程中關閉。
例子:
#include int main(int argc, char *argv[]) {char *envp[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};char *argv_execv[] = {"echo", "executed by execv", NULL};char *argv_execvp[] = {"echo", "executed by execvp", NULL};char *argv_execve[] = {"env", NULL};if (fork() == 0) {if (execvl("/bin/echo", "echo", "executed by execl", NULL) < 0)perror("Err on execl");}if (fork() == 0) {if (execlp("echo", "echo", "executed by execlp", NULL) < 0) perror("Err on execlp");}if (fork() == 0) {if (execle("/usr/bin/env", "env", NULL, envp) < 0) perror("Err on execle");}if (fork() == 0) {if (execv("/bin/echo", argv_execv) < 0) perror("Err on execv");}if (fork() == 0) {if (execvp("echo", argv_execvp) < 0)perror("Err on execvp");}if (fork() == 0) {if (execve("/usr/bin/env", argv_execve, envp) < 0) perror("Err on execve");} }?
總結
以上是生活随笔為你收集整理的[shell]C语言调用shell脚本接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS转义字符对照表
- 下一篇: 带超时的system