shell編程教程
A. 簡單的shell編程
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFERSIZE 80
extern char *get_current_dir_name(void);
extern char *getenv(const char *name);
extern pid_t waitpid(pid_t pid, int *status, int options);
char buffer[BUFFERSIZE+1];
main()
{
char *path, *arg[10], *input;
int li_inputlen, is_bj, is_back, i, j, k, pid, status;
char lc_char;
while (1){
/* initiations */
is_bj = 0; /*redirection flag*/
is_back = 0; /*background*/
/* shell prompt */
path = get_current_dir_name();
printf("%s>$",path);
/*開始獲取輸入*/
li_inputlen = 0;
lc_char = getchar();
while (lc_char !='\n'){
if(li_inputlen < BUFFERSIZE)
buffer[li_inputlen++] = lc_char;
lc_char = getchar();
}
/*命令超長處理*/
if (li_inputlen >= BUFFERSIZE){
printf("Your command is too long! Please re-enter your command!\n");
li_inputlen = 0; /*reset */
continue;
}
else
buffer[li_inputlen] = '\0';/*加上串結束符號,形成字串*/
/*將命令從緩存拷貝到input中*/
input = (char *) malloc(sizeof(char) * (li_inputlen+1));
strcpy(input,buffer);
/* 獲取命令和參數並保存在arg中*/
for (i = 0,j = 0,k = 0;i <= li_inputlen;i++){
/*管道和重定向單獨處理*/
if (input[i] == '<' || input[i] == '>' || input[i] =='|'){
if (input[i] == '|')
pipel(input,li_inputlen);
else
redirect(input,li_inputlen);
is_bj = 1;
break;
}
/*處理空格、TAB和結束符。不用處理『\n',大家如果仔細分析前面的獲取輸入的程序的話,
*不難發現回車符並沒有寫入buffer*/
if (input[i] == ' ' || input[i] =='\t' || input[i] == '\0'){
if (j == 0) /*這個條件可以略去連在一起的多個空格或者tab*/
continue;
else{
buffer[j++] = '\0';
arg[k] = (char *) malloc(sizeof(char)*j);
/*將指令或參數從緩存拷貝到arg中*/
strcpy(arg[k],buffer);
j = 0; /*准備取下一個參數*/
k++;
}
}
else{
/*如果字串最後是『&',則置後台運行標記為1*/
if (input[i] == '&' && input[i+1] == '\0'){
is_back = 1;
continue;
}
buffer[j++] = input[i];
}
}
free(input);/*釋放空間*/
/*如果輸入的指令是leave則退出while,即退出程序*/
if (strcmp(arg[0],"leave") == 0 ){
printf("bye-bye\n");
break;
}
/*如果輸入的指令是about則顯示作者信息,同時結束本條命令的解析過程*/
if (strcmp(arg[0]," about") == 0 ){
printf("right by shike,[email protected]\n");
continue;
}
if (is_bj == 0){ /*非管道、重定向指令*/
/*在使用xxec執行命令的時候,最後的參數必須是NULL指針,
*所以將最後一個參數置成空值*/
arg[k] = (char *) 0;
/*判斷指令arg[0]是否存在*/
if (is_fileexist(arg[0]) == -1 ){
printf("This command is not found?!\n");
for(i=0;i<k;i++)
free(arg[i]);
continue;
}
/* fork a sub-process to run the execution file */
if ((pid = fork()) ==0) /*子進程*/
execv(buffer,arg);
else /*父進程*/
if (is_back == 0) /*並非後台執行指令*/
waitpid(pid,&status,0);
/*釋放申請的空間*/
for (i=0;i<k;i++)
free(arg[i]);
}
}
}
int is_fileexist(char *comm)
{
char *path,*p;
int i;
i = 0;
/*使用getenv函數來獲取系統環境變數,用參數PATH表示獲取路徑*/
path = getenv("PATH");
p = path;
while (*p != '\0'){
/*路徑列表使用『:』來分隔路徑*/
if (*p != ':')
buffer[i++] = *p;
else{
buffer[i++] = '/';
buffer[i] = '\0';
/*將指令和路徑合成,形成pathname,並使用access函數來判斷該文件是否存在*/
strcat(buffer,comm);
if (access(buffer,F_OK) == 0) /*文件被找到*/
return 0;
else
/*繼續尋找其它路徑*/
i = 0;
}
p++;
}
/*搜索完所有路徑,依然沒有找到則返回-1*/
return -1;
}
int redirect(char *in,int len)
{
char *argv[30],*filename[2];
pid_t pid;
int i,j,k,fd_in,fd_out,is_in = -1,is_out = -1,num = 0;
int is_back = 0,status=0;
/*這里是重定向的命令解析過程,其中filename用於存放重定向文件,
*is_in, is_out分別是輸入重定向標記和輸出重定向標記*/
for (i = 0,j = 0,k = 0;i <= len;i++){
if (in[i]==' '||in[i]=='\t'||in[i]=='\0'||in[i] =='<'||in[i]=='>'){
if (in[i] == '>' || in[i] == '<'){
/*重定向指令最多'<','>'各出現一次,因此num最大為2,
*否則認為命令輸入錯誤*/
if (num < 3){
num ++;
if (in[i] == '<')
is_in = num - 1;
else
is_out = num - 1;
/*處理命令和重定向符號相連的情況,比如ls>a*/
if (j > 0 && num == 1) {
buffer[j++] = '\0';
argv[k] = (char *) malloc(sizeof(char)*j);
strcpy(argv[k],buffer);
k++;
j = 0;
}
}
else{
printf("The format is error!\n");
return -1;
}
}
if (j == 0)
continue;
else{
buffer[j++] = '\0';
/*尚未遇到重定向符號,字元串是命令或參數*/
if (num == 0){
argv[k] = (char *) malloc(sizeof(char)*j);
strcpy(argv[k],buffer);
k++;
}
/*是重定向後符號的字元串,是文件名*/
else{
filename[status] = (char *) malloc(sizeof(char)*j);
strcpy(filename[status++],buffer);
}
j = 0; /*initate*/
}
}
else{
if (in[i] == '&' && in[i+1] == '\0'){
is_back = 1;
continue;
}
buffer[j++] = in[i];
}
}
argv[k] = (char *) 0;
if (is_fileexist(argv[0]) == -1 ){
printf("This command is not founded!\n");
for(i=0;i<k;i++)
free(argv[i]);
return 0;
}
if ((pid = fork()) ==0){
/*存在輸出重定向*/
if (is_out != -1)
if((fd_out=open(filename[is_out],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1){
printf("Open out %s Error\n",filename[is_out]);
return -1;
}
/*存在輸入重定向*/
if (is_in != -1)
if((fd_in=open(filename[is_in],O_RDONLY,S_IRUSR|S_IWUSR))==-1){
printf("Open in %s Error\n",filename[is_out]);
return -1;
}
if (is_out != -1)
/*使用p2函數將標准輸出重定向到fd_out上,p2(int oldfd,int newfd)實現的
*是把oldfd所指的文件描述符復制到newfd。若newfd為一已打開的文件描述詞,
*則newfd所指的文件會先被關閉,p2復制的文件描述詞與原來的文件描述詞
*共享各種文件狀態*/
if(p2(fd_out,STDOUT_FILENO)==-1){
printf("Redirect Standard Out Error\n");
exit(1);
}
if (is_in != -1)
if(p2(fd_in,STDIN_FILENO)==-1){
printf("Redirect Standard Out Error\n");
exit(1);
}
execv(buffer,argv);
}
else
if (is_back == 0) /*run on the TOP*/
waitpid(pid,&status,0);
for (i=0;i<k;i++)
free(argv[i]);
if (is_in != -1){
free(filename[is_in]);
close(fd_in);
}
if (is_out != -1){
free(filename[is_out]);
close(fd_out);
}
return 0;
}
int pipel(char *input,int len)
{
char *argv[2][30];
int i,j,k,count,is_back = 0;
int li_comm = 0,fd[2],fpip[2];
char lc_char,lc_end[1];
pid_t child1,child2;
/*管道的命令解析過程*/
for (i = 0,j = 0,k = 0;i <= len;i++){
if (input[i]== ' ' || input[i] == '\t' || input[i] == '\0' || input[i] == '|'){
if (input[i] == '|' ) /*管道符號*/
{
if (j > 0)
{
buffer[j++] = '\0';
/*因為管道連接的是兩個指令,所以用二維數組指針來存放命令和參數,
*li_comm是表示第幾個指令*/
argv[li_comm][k] = (char *) malloc(sizeof(char)*j);
strcpy(argv[li_comm][k++],buffer);
}
argv[li_comm][k++] = (char *) 0;
/*遇到管道符,第一個指令完畢,開始准備接受第二個指令*/
li_comm++;
count = k;
k=0;j=0;
}
if (j == 0)
continue;
else
{
buffer[j++] = '\0';
argv[li_comm][k] = (char *) malloc(sizeof(char)*j);
strcpy(argv[li_comm][k],buffer);
k++;
}
j = 0; /*initate*/
}
else{
if (input[i] == '&' && input[i+1] == '\0'){
is_back = 1;
continue;
}
buffer[j++] = input[i];
}
}
argv[li_comm][k++] = (char *) 0;
if (is_fileexist(argv[0][0]) == -1 ){
printf("This first command is not found!\n");
for(i=0;i<count;i++)
free(argv[0][i]);
return 0;
}
/*指令解析結束*/
/*建立管道*/
if (pipe(fd) == -1 ){
printf("open pipe error!\n");
return -1;
}
/*創建第一個子進程執行管道符前的指令,並將輸出寫到管道*/
if ((child1 = fork()) ==0){
/*關閉讀端*/
close(fd[0]);
if (fd[1] != STDOUT_FILENO){
/*將標准輸出重定向到管道的寫入端,這樣該子進程的輸出就寫入了管道*/
if (p2(fd[1],STDOUT_FILENO) == -1){
printf("Redirect Standard Out Error\n");
return -1;
}
/*關閉寫入端*/
close(fd[1]);
}
execv(buffer,argv[0]);
}
else{ /*父進程*/
/*先要等待寫入管道的進程結束*/
waitpid(child1,&li_comm,0);
/*然後我們必須寫入一個結束標記,告訴讀管道進程數據到這里就完了*/
lc_end[0] = 0x1a;
write(fd[1],lc_end,1);
close(fd[1]);
if (is_fileexist(argv[1][0]) == -1 ){
printf("This command is not founded!\n");
for(i=0;i<k;i++)
free(argv[1][i]);
return 0;
}
/*創建第二個進程執行管道符後的指令,並從管道讀輸入流 */
if ((child2 = fork()) == 0){
if (fd[0] != STDIN_FILENO){
/*將標准輸入重定向到管道讀入端*/
if(p2(fd[0],STDIN_FILENO) == -1){
printf("Redirect Standard In Error!\n");
return -1;
}
close(fd[0]);
}
execv(buffer,argv[1]);
}
else /*父進程*/
if (is_back == 0)
waitpid(child2,NULL,0);
}
for (i=0;i<count;i++)
free(argv[0][i]);
for (i=0;i<k;i++)
free(argv[1][i]);
return 0;
}
以前寫的,好像一些細節不一樣,不明白的地方,發郵件給我,[email protected]
B. 《linux命令行與shell腳本編程大全》pdf下載在線閱讀全文,求百度網盤雲資源
《Linux命令行與shell腳本編程大全》網路網盤pdf最新全集下載:
鏈接: https://pan..com/s/1E_h5bBXPM-pZR2jFGctrgA
簡介:這是一本關於Linux命令行與shell腳本編程的全方位教程,主要包括四大部分:Linux命令行,shell腳本編程基礎,高級shell腳本編程,如何創建實用的shell腳本。本書針對Linux系統的新特性進行了全面更新,不僅涵蓋了詳盡的動手教程和現實世界中的實用信息,還提供了與所學內容相關的參考信息和背景資料。通過本書的學習,你將輕松寫出自己的shell腳本。

C. shell腳本是什麼
當執行命令或程序語句是通過程序文件而不是命令行,那這個程序被稱為Shell腳本。如果Shell腳本內置很多命令、語句及循環控制,然後一次性執行完畢,這種通過文件執行腳本的方式稱為非交互方式。用戶可以在Shell腳本中輸入一系列命令及命令語句組合。這些命令、變數和流程式控制制語句等有機地結合在一起,就形成一個功能強大的Shell腳本。
D. 『Linux 干貨』#1 終端與Shell(簡明)
繼 Git 後貴系的另一個暑培項目,講授 Linux 的基本用法,恰好這學期「操作系統」課程實驗需要用到 Linux,而且實驗室的伺服器也需要學習相關用法,故學之。
本文部分內容參考了清華 ZAH 同學的教程,部分參考了 劉遄 老師的《 Linux 就該這么學 》, 菜鳥教程-Linux 。
Linux,全稱 GNU/Linux,是一套免費使用和自由傳播的類 Unix 操作系統。相比於其他系統,Linux 更加穩定且有效率、更加安全、相對不耗資源……以至於幾乎所有 長期穩定運行的網站伺服器 上、在 處理大數據的集群系統 中,以及需要 協同工作的伺服器環境 都採用 Linux 系統。
Linux 嚴格來說是單指操作系統的 內核 ,因操作系統中包含了許多用戶圖形介面和其他實用工具。如今 Linux 常用來指「基於 Linux 的完整操作系統」,內核則改以「Linux 內核」稱之。
一些組織或廠商將 Linux 內核與各種軟體和文檔 包裝 起來,並提供系統安裝界面和系統配置、設定與管理工具,就構成了 Linux 的發行版本。
在學習 Linux 的過程中,有幾個易混淆的概念:
命令行界面(Command-Line Interface,CLI)是在圖形用戶界面得到普及之前使用最為廣泛的用戶界面,它通常不支持滑鼠,用戶通過鍵盤輸入指令,計算機接收到指令後,予以執行。也有人稱之為字元用戶界面(Character User Interface,CUI)。
一般來說,在 伺服器 中較多採用的是 CLI 界面,或許有以下幾點原因:
Shell 是一個用 C 語言編寫的程序,它是用戶使用 Linux 等系統的橋梁,如同「 殼 」一般。它的本質是一個 命令解釋器 ,將用戶輸入的命令(符合 Shell 語法)處理成對應 操作系統的控制命令 ,處理完畢後再將結果反饋給用戶。
不同操作系統下面的 Shell 種類眾多,常見的有:
Ken Thompson 的 sh 是第一種 Unix Shell,本教程關注的是 Bash,也就是 Bourne Again Shell,Bash 也是大多數 Linux 系統默認的 Shell。
終端 (Terminal),是一種用來讓用戶輸入數據至計算機,以及顯示其計算結果的機器。早期的終端通常就是一台 電子打字機 (Teletypewriter, TTY),後來隨著計算機的發展,打字機被鍵盤和顯示器取代,而 GUI 界面也成了主流。
於是,這時候我們就需要一個程序來模擬傳統終端的行為,即 終端模擬器 (Terminal Emulator),當用戶打開終端模擬器時,實際上是進入一個 會話進程 (Session)。終端模擬器有很多,這里舉幾個經典的例子:
在 Linux 系統中打開終端時,會看到一個提示符,通常類似 hewei@hewei-VirtualBox ~$ 。在提示符下,命令會被 Shell 環境 解析並反饋 到終端中。
提示符是 Shell 最主要的 文本介面 。它告訴你,你的主機名是 hewei-VirtualBox ,你現在的身份是 hewei 並且你當前的 工作目錄 (Current working directory)是 ~ (默認在 /home/hewei/ 用戶目錄)。
$ 符號表示您現在的身份不是 root ,輸入如下命令可以暫時切換到 root 許可權:
輸入密碼後,可以看到提示符變成了 root@hewei-VirtualBox:/home/hewei# ,其中 # 符號就是超級用戶許可權的標志。再輸入 exit 即可退回普通用戶身份。
在 Git學習筆記 #1 基礎知識介紹 中,已經簡單介紹了命令行界面的一些使用技巧,這些命令在 Linux 系統的 Bash 中同樣使用。這里羅列出 Linux 常用快捷鍵:
常見的執行 Linux 命令的格式是這樣的: 命令名稱 [命令參數] [命令對象] 。其中,命令參數用於對命令進行調整,使之更好地貼近需求,參數分為 長格式 和 短格式 ,如: man --help , man -h 。短格式之間可以合並,合並後僅保留一個減號即可。
在 Linux 相關的手冊中,我們會約定俗成地將可選擇的、非必需的參數使用 中括弧 引起來,而命令所要求的、必須有的參數或對象值,則不帶中括弧。
此外,要注意 Linux 系統中的命令、參數、對象都是 嚴格區分大小寫 的。
Shell 除了是一個 互動式 (Interactive)的命令解釋器,它還是一種 程序設計語言 (Shell Script)。它定義了各種變數和參數,並提供了許多在高級語言中才具有的控制結構,包括循環和分支。
用 Shell 編寫的 腳本文件 即 .sh 文件,它能在 Shell 環境下運行,fork 出一個 子進程 ,調用系統內核來執行 批處理 (Batch)的系統控制。在文件的第一行,通常是 #!/bin/bash ,這句話約定了這個腳本需要哪種 Shell 環境來執行。
通過如下命令就可以執行一個 Shell 腳本:
下面羅列了部分常用指令與參數的介紹,更多功能請在幫助手冊中檢索。
E. 學生信的那些事兒之七 - Linux基礎之Shell腳本編程
沿著前面的軌跡,接下來是Linux中shell腳本的學習。這對於生信工程師後續處理大量 (海量更合適些) 數據是非常非常重要的,但是同樣的,作為一個有點古板的人,對於"腳本"是什麼意思我都死磕了好久。主要覺得有些抽象,尤其是跟生信的同事討論項目分析部分的問題時,他們經常會說道這個詞,在他們意識里這是個不言自明的術語,殊不知對外行人而言 (比如我),那簡直就是無情的"知識的詛咒"。經常是我假裝聽懂了,然後繼續討論下面的問題,形成一個模糊的印象。
網路上的解釋是:腳本(Script)是一種批處理文件的延伸,是一種純文本保存的程序,一般來說的計算機腳本程序是確定的一系列控制計算機進行運算操作動作的組合,在其中可以實現一定的邏輯分支等。不知道你能不能看懂,反正我開始的時候真是一知半解。
鳥哥私房菜的解釋是:shell script是利用 shell 的功能所寫的一個"程序",這個程序是使用純文本文件,將一些shell的語法與命令(含外部命令)寫在裡面,搭配正則表達式、管道命令與數據流重定向等功能,以達到我們所想要的處理的目的。不明覺厲,好像更看不懂了···
Jude 的簡單粗暴大白話解釋是:腳本就是Linux中很多命令按照一定規則的組合,以實現某個特定的功能。Linux中有很多簡單的命令,往往只是進行了簡單的對話,比如 cd 就是進入到某個目錄,簡單直接。但是如果我想進入某個目錄A,然後在目錄A中創建目錄B,再在目錄B中創建文本C呢?當然可以一步一步操作,如果想要一步到位呢,那就可以用腳本,把三個命令寫在一起,一起執行。好像有點啰嗦···
或者從英語的角度去理解,腳本的對應英文是Script,而這個單詞的中文釋義中還有劇本的意思。劇本就好理解了啊,劇本就是導演(生信工程師)基於某個主旨(要實現的目標)按照一定的手法(規則)所寫的一個故事。不管是哪個演員,都得按照劇本演。所以,學好英語對於生信也是有幫助的~
按照腳本的復雜程度可以分為:
這個無需多說,其實就是若干個簡單命令的順序排列,執行腳本後會按照命令的前後關系從前往後一一執行。
相對於簡單的基本腳本,結構化的命令腳本可以施加邏輯流程式控制制,從而改變程序(命令)執行的順序。基本腳本中的命令就是從上往下執行,但是結構化的命令腳本可以根據邏輯判斷重復或者跳過某些命令。
常用的結構化命令(語句)有:
後面還有什麼嵌套循環啊啥的,不過我覺得上面的7中命令學到家了,應該可以應付大部分在生信分析裡面的應用了。
記得高中的時候,物理老師(也是班主任)在給我們講解習題時有個有意思的套路:不管什麼難題現在下面寫個"答:",以示自己解決問題的決心,也是一種正向的心理暗示。腳本編寫也是有套路的,不過總的來說還是比較簡單。
對於簡單的腳本(超級簡單的那種),直接幾個命令連在一起即可,中間用";"隔開。
對於更長更復雜的腳本,一般需要創建一個文本,並在裡面編輯。這就涉及到了文本編輯器,比較常用和簡單的一般有nano和vim,實在很簡單,規則也容易理解,教程隨手可得,不多說。
比如用vim創建了一個腳本之後,具體的語法(套路):
ok,腳本寫完了,怎麼讓腳本開始工作呢?這有涉及到之前講過的環境變數和相對路徑、絕對路徑了。方法有三:
就這么多吧,應該有點感覺到了,剩下的就是狂練狂練了~
F. Linux Shell 教程——想玩轉linux就請一直看下去
Shell 是一個用 C 語言編寫的程序,它是用戶使用 Linux 的橋梁。Shell 既是一種命令語言,又是一種程序設計語言。
Shell 是指一種應用程序,這個應用程序提供了一個界面,用戶通過這個界面訪問操作系統內核的服務。
Ken Thompson 的 sh 是第一種 Unix Shell,Windows Explorer 是一個典型的圖形界面 Shell。
Shell 在線工具
Shell 腳本(shell script),是一種為 shell 編寫的腳本程序。
業界所說的 shell 通常都是指 shell 腳本,但讀者朋友要知道,shell 和 shell script 是兩個不同的概念。
由於習慣的原因,簡潔起見,本文出現的 "shell編程" 都是指 shell 腳本編程,不是指開發 shell 自身。
Shell 編程跟 JavaScript、php 編程一樣,只要有一個能編寫代碼的文本編輯器和一個能解釋執行的腳本解釋器就可以了。
Linux 的 Shell 種類眾多,常見的有:
在一般情況下,人們並不區分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh ,它同樣也可以改為 #!/bin/bash 。
#! 告訴系統其後路徑所指定的程序即是解釋此腳本文件的 Shell 程序。
打開文本編輯器(可以使用 vi/vim 命令來創建文件),新建一個文件 test.sh,擴展名為 sh(sh代表shell),擴展名並不影響腳本執行,見名知意就好,如果你用 php 寫 shell 腳本,擴展名就用 php 好了。
輸入一些代碼,第一行一般是這樣:
#!/bin/bash
echo "Hello World !"
運行實例 »
#! 是一個約定的標記,它告訴系統這個腳本需要什麼解釋器來執行,即使用哪一種 Shell。
echo 命令用於向窗口輸出文本。
1、作為可執行程序
將上面的代碼保存為 test.sh,並 cd 到相應目錄:
注意,一定要寫成 ./test.sh ,而不是 test.sh ,運行其它二進制的程序也一樣,直接寫 test.sh,linux 系統會去 PATH 里尋找有沒有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的當前目錄通常不在 PATH 里,所以寫成 test.sh 是會找不到命令的,要用 ./test.sh 告訴系統說,就在當前目錄找。
2、作為解釋器參數
這種運行方式是,直接運行解釋器,其參數就是 shell 腳本的文件名,如:
這種方式運行的腳本,不需要在第一行指定解釋器信息,寫了也沒用。
