本文目錄一覽:
C語言 shell命令
可以通過system函數,調用shell命令。
1 函數原型:
int system(const char *cmd);
2 功能:
調用cmd內容的系統命令,即shell命令。
3 頭文件:
stdlib.h
4 舉例:
system(“ls”);
列印當前工作目錄下的文件。
如何在C語言中執行shell命令
題主可以使用 exec 系列函數。這系列函數定義在 unistd.h 頭文件中,所以使用前請包含這個頭文件。這系列函數共有五個,
execl, execlp, execv, execvp, execle
其中常用的是前四個。前四個函數的原型為:
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
這四個函數的主要差別就在於參數的類型和用不用輸入命令的絕對路徑上。
以路徑形式來分,凡是函數名中帶 p 的(execlp,execvp)都只需要提供命令的名,函數會自動在當前的環境變數 $PATH 中查找命令的路徑。而不帶 p 的(execl,execv)必須要提供命令的絕對路徑,否則函數會找不到這個命令的位置。這裡以 execl 和 execlp 為例,以下運行
ls -l
命令的代碼:
#include stdio.h
#include unistd.h
int main() {
// exec 系列函數出錯時會返回 -1,平常返回 0,所以可以
// 據此來列印錯誤信息
// 第一個 ls 是命令的名稱,execlp 函數會自動在 $PATH
// 中尋找這個命令。
// 後面一個 ls 是要在 shell 中輸入的第一個參數
//(也就是命令名稱本身)
// 使用 NULL 作為參數結尾標記是 exec 系列函數的要求。
if (execlp(“ls”, “ls”, “-l”, NULL) == -1)
perror(“Error Executing Command.\n”);
return 0;
}
在 shell 中運行這個 C 程序會輸出
和你直接在 shell 中寫 ls -l 的效果是一樣的。然而,如果你使用不帶 p 的 execl, 那麼這樣寫就會報錯。
#include stdio.h
#include unistd.h
int main() {
// execl 只接受命令的絕對路徑,所以必須輸入完整的
// 路徑 /bin/ls,即
// if (execl(“/bin/ls”, “ls”, “-l”, NULL) == -1)
if (execl(“ls”, “ls”, “-l”, NULL) == -1)
perror(“Error Executing Command.\n”);
return 0;
}
輸出結果為:
以參數類型來分,凡是函數名中帶 l 的(execl,execlp)都需要把系統命令的參數全部傳遞給函數,而凡是函數名中帶 v 的(execv,execvp)都需要把系統命令的參數統一放在一個數組裡,然後把這個數組傳遞給函數。
比如剛才這個
#include stdio.h
#include unistd.h
int main() {
if (execlp(“ls”, “ls”, “-l”, NULL) == -1)
perror(“Error Executing Command.\n”);
return 0;
}
如果改用 execvp 來寫的話就是
#include stdio.h
#include unistd.h
int main() {
// 這個字元串數組存有所有參數(包括命令名本身和
// 最後的 NULL)
char * argv[] = {“ls”, “-l”, NULL};
// 這裡只需將命令名稱和參數數組傳遞給 execvp 函數即可,
// 無需將參數一個個傳遞。同樣函數會自動在 $PATH
// 中查找命令
if (execvp(“ls”, argv) == -1)
perror(“Error Executing Command.\n”);
return 0;
}
運行結果同樣和直接寫 ls -l 的效果相同。
execv 和 execvp 的區別也在於是否必須輸入絕對路徑,就不贅述了。
要注意的一點是,如果執行成功,exec 系列函數開啟的新進程會完全代替當前的進程,也就是說當前進程會消失。所以一般會將 exec 和 fork 連用,先 fork 出一個子進程,然後在這個子進程中使用 exec 運行別的程序,防止父進程被 exec 覆蓋。比如剛才的代碼稍微改一下
#include stdio.h
#include unistd.h
int main() {
char * argv[] = {“ls”, “-l”, NULL};
if (execvp(“ls”, argv) == -1)
perror(“Error Executing Command.\n”);
// 加入一個 printf 語句
printf(“Main process is still running.\n”);
return 0;
}
運行後並不會出現 Main process is still running 這句話,因為 exec 後 main 函數執行產生的進程已經被 ls 命令產生的進程完全覆蓋了,所以 exec 函數以下的語句是完全不會執行的。這時就可以使用 fork 來新建一個子進程,在子進程中執行 exec 函數。
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/wait.h
int main() {
int r;
// fork() 大於零,父進程
if ((r = fork()) 0) {
int status;
if (wait(status) != -1) {
// 等待子進程退出
if(WIFEXITED(status)) {
printf(“Main process is still running.\n”);
return 0;
}
}
// fork () 等於零,子進程。子進程中運行 exec
} else if (r == 0) {
char * argv[] = {“ls”, “-l”, NULL};
if (execvp(“ls”, argv) == -1)
perror(“Error Executing Command.\n”);
return 0;
// fork() 小於零,出錯
} else {
perror(“Fork”);
}
return 0;
}
這樣運行結果就變成了
Main process is still running 這句話就會被輸出到屏幕上。
linux下怎樣用c語言調用shell命令
C程序調用shell腳本共同擁有三種法子 :system()、popen()、exec系列數call_exec1.c ,
system() 不用你自己去產生進程。它已經封裝了,直接增加自己的命令
exec 須要你自己 fork 進程,然後exec 自己的命令
popen() 也能夠實現運行你的命令,比system 開銷小
方法一、system()的使用。我直接上代碼吧
int system(const char *command);
我在/home/book/shell新建一個test.sh文件例如以下:
span style=”font-size:18px;”span style=”font-size:18px;”#!bin/bash
echo $HOME
echo “the is test!”/span/span
test.c文件例如以下:
span style=”font-size:18px;”span style=”font-size:18px;”#includestdlib.h
int main()
{
system(“bash /home/book/shell/test.sh”); /* chmod +x test.sh ,路徑前面要加上bash */
return 0;
}/span/span
運行例如以下命令來編譯:
span style=”font-size:18px;”gcc test.c -o test
/span
測試命令:
span style=”font-size:18px;”./test/span
結果例如以下:
span style=”font-size:18px;”/root
the is test!/span
方法二:popen() 會調用fork()產生 子歷程,然後從子歷程中調用/bin/sh -c來履行 參數command的指令。參數type可應用 「r」代表讀取。「w」代表寫入。遵循此type值。popen()會建立 管道連到子歷程的標準 輸出設備 或標準 輸入設備 ,然後返回一個文件指針。
隨後歷程便可利用 此文件指針來讀取子歷程的輸出設備 或是寫入到子歷程的標準 輸入設備 中。此外,全部應用 文 件指針(FILE*)操作的函數也都能夠應用 ,除了fclose()以外。
返回值:若成功 則返迴文件指針,否則返回NULL,差錯 原因存於errno中。注意:在編寫具SUID/SGID許可權的程序時請盡量避免應用 popen()。popen()會繼承環境變數。通過環境變數可能會造成系統安全的問題
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
其它不用改變我們直接改動test.c文件:
#includestdio.h
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;
}
方法三:exec函數簇 (我不太懂,copy別人的。也沒有驗證。習慣方法一)
須要注意的是exec並非1個函數, 事實上它僅僅是一組函數的統稱, 它包含以下6個函數:
#include unistd.h
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個部分, 運行文件部分, 命令參數部分, 環境變數部分.
比如我要運行1個命令 ls -l /home/gateman
運行文件部分就是 “/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 興許, 命令參數部分必須是1個以NULL結尾的字元串指針數組的頭部指針. 比如char * pstr就是1個字元串的指針, char * pstr[] 就是數組了, 分別指向各個字元串.
關於Linux命令的介紹,看看《linux就該這麼學》,具體關於這一章地址3w(dot)linuxprobe/chapter-02(dot)html
p興許, 運行文件部分能夠不帶路徑, exec函數會在$PATH中找
還有1個注意的是, exec函數會代替運行它的進程, 也就是說, 一旦exec函數運行成功, 它就不會返回了, 進程結束. 可是假設exec函數運行失敗, 它會返回失敗的信息, 並且進程繼續運行後面的代碼!
通常exec會放在fork() 函數的子進程部分, 來替代子進程運行啦, 運行成功後子程序就會消失, 可是運行失敗的話, 必須用exit()函數來讓子進程退出!
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/245479.html