shell腳本編程:
編程語言的分類:根據運行方式
編譯運行:源代碼----->編譯器(編譯)---->程序文件
C語言:
解釋運行:源代碼--->運行時啓動解釋器,由解釋器邊解釋邊運行;
根據其編程過程中功能的實現是調用庫還是調用外部的程序文件:
shell腳本編程:
利用系統上的命令及編程組件進行編程;
完成編程:
利用庫或編程組件運行編程
編程模型:
過程式編程語言,面向對象的編程語言
程序=指令+數據
過程式:以指令爲中心來組織代碼,數據是服務於代碼;
順序執行
選擇執行
循環執行
(C語言,bash腳本語言)
對象式:以數據爲中心來組織代碼,圍繞數據來組織指令;
類(class):實例化對象,method;
(JAVA,C++,Python)
shell腳本編程:(特性)
(1)過程式編程
(2)解釋運行
(3)依賴於外部程序文件
如何寫shell腳本:
腳本文件的第一行,頂格寫,給出shebang,解釋器路徑,用於指明解釋執行當前腳本的解釋器程序文件
常見的解釋器:
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
文本編輯器:nano
行編輯器:sed
全屏幕編輯器:nano,vim,vi
使用namo命令:
ctrl+G:求助 ctrl+O:寫入 ctrl+R:讀檔 ctrl+Y:上頁 ctrl+V:下頁
ctrl+K:剪切文字 ctrl+U:還原剪切 ctrl+C:遊標位置 ctrl+T:拼寫檢查
ctrl+X:離開 ctrl+J:對齊
一般而言shell腳本都以“.sh”結尾。
shell腳本是什麼?
命令的堆積;
但是很多命令不具有冪等性,需要用程序邏輯來判斷運行條件是否滿足,以避免其運行 中發生錯誤;
編寫第一個shell腳本:
nanomyshell.sh
腳本內容爲:
#!/bin/bash
useradduser3
echo"user3" | passwd --stdin user3
mktemp-d /tmp/test.XXX
最後腳本寫完後,直接按“ctrl+X”保存退出
要想執行腳本,先看一下這個腳本的權限
[root@centos6~]# ls -l myshell.sh
-rw-r--r--.1 root root 86 Oct 26 22:51 myshell.sh
再看當前用戶是以什麼身份登入的:
[root@centos6~]# whoami
root
給用戶加執行權限:
[root@centos6~]# chmod +x myshell.sh
[root@centos6~]# ls -l myshell.sh
-rwxr-xr-x.1 root root 86 Oct 26 22:51 myshell.sh
[root@centos6~]#
最後執行腳本:
[root@centos6 ~]# ./myshell.sh
Changing password for user user3.
passwd: all authentication tokens updatedsuccessfully.
/tmp/test.noG
[root@centos6 ~]#
注意當我們再次運行上面的腳本時,我們的腳本還會執行,只是第一個創建用戶的命令創建失敗了,下面的兩條命令執行成功了。
這說明,在shell腳本中並不是所有的命令失敗,都會導致腳本終止的,而是命令執行失敗,如果產生嚴重的錯誤,這種嚴重的錯誤比如會“exit”,一般而言任何腳本遇到“exit”都會終止或者我們使用某種判斷機制將其強行終止。或者我們的腳本運行中出現了語法錯誤。腳本纔有可能終止。
所以我我們可以將上面的腳本創建用戶時,加一個判斷,用戶在就不創建,不在就創建,那麼就是將第一個命令改爲“id user3||useradd user3”這樣再次執行腳本就不會報錯了
運行腳本:
(1)賦予執行權限,並執行運行此程序文件;
chmod +x /PATH/TO/SCRIPT_FILE //一加x所有的用戶都有了執行權限
./SCRIPT_FILE //相對路徑
/PATH/TO/SCRIPT_FILE //絕對路徑
(2)直接運行解釋器,將腳本以命令行參數傳遞給解釋器程序
bash /PATH/TO/SCRIPT_FILE
(解釋:
使用解釋器運行腳本,本質上其實就是利用了linux內核有一個特性,他可以通過文本的前n個字節,來判定這是一個什麼格式的文件,尤其是看到文件前兩個字節是“#!”就知道後面是shell腳本程序文件,我們不需要直接運行這個文件,而是先運行“#!”後面的解釋器程序,然後由這個解釋器程序來解釋運行這個文件中的程序代碼。)
演示:
[root@centos6 ~]# ll myshell.sh
-rw-r--r--. 1 root root 98 Oct 26 23:12myshell.sh
[root@centos6 ~]# bash myshell.sh
uid=502(user3) gid=502(user3)groups=502(user3)
Changing password for user user3.
passwd: all authentication tokens updatedsuccessfully.
/tmp/test.BkL
[root@centos6 ~]#
這也說明了,shell是嚴重依賴命令的,不然怎麼說shell腳本是命令的堆積呢
練習1:寫一個腳本,實現如下功能:
(1)顯示/etc/目錄下所有以大寫P或小寫p開頭的文件或目錄本身;
(2)顯示/var目錄下的所有文件或目錄本身,並將顯示結果中的小寫字母轉換爲大寫後顯示:
(3)創建臨時文件/tmp/myfile.XXXX;
解:
#!/bin/bash
ls -d /etc/[Pp]*
ls -d /var/* | tr [:lower:] [:upper:]
或
ls -d /var/* | tr 'a-z' 'A-Z'
mktemp /tmp/myfile.XXXX
(拓展:
上面的腳本我們還可以進行優化:
上面的腳本執行過程中沒有執行後的反饋,我們可以進一步的進行完善:
如下:
#!/bin/bash
echo "Show some under /etc" //echo的內容都是直接顯示在屏幕上
ls -d /etc/[Pp]*
echo "Traslate lower to upper"
ls -d /var/* | tr 'a-z' 'A-Z'
echo "Create a temp file"
mktemp /tmp/myfile.XXXX
)
注意:
(1)腳本中的空白行直接被忽略,所以不會輸出空白行
(2)腳本中,除了shebang,餘下所有以#開頭的行,都會被視作註釋行而被忽略;此 即爲註釋行
(3)如果想從腳本中輸出一個空白行,則在腳本中加上一行,並且這行中只寫一個echo
(4)shell腳本的運行是通過運行一個子shell進程實現的;
bash的配置文件:(定義別名)
(我們之前講的,在shell進程中定義的別名,當我們的系統重啓以後,當我們的shell登出再登錄以後,我們在shell下定義的特性,包括變量,他的生命週期僅是當前shell進程,如果想突破這樣的生命週期,我們應該在他的配置文件中定義)
bash程序有兩大類的配置文件:
profile類
bashrc類
profile類:爲交互式登錄的shell進程提供配置
bashrc類:爲非交互式登錄的shell進程提供配置
登錄類型:
交互式登錄shell進程;
直接通過某終端輸入賬號和密碼後登錄打開的shell進程;
使用su命令:su -USERNAME,或者使用 su -l USERNAME執行的登錄切換;
非交互式登錄shell進程:
suUSERNAME執行的登錄切換;
圖形界面下打開的終端;
運行腳本時
profile類:
全局:對所有用戶都生效;
/etc/profile
/etc/profile.d/*.sh
用戶個人:僅對當前用戶有效;
~/.bashrc_profile
功能:
(1)用於定義環境變量;
(2)運行命令或腳本;
bashrc類:
全局:
/etc/bashrc
用戶個人:
~/.bashrc
功用:
(1)定義本地變量;
(2)定義命令別名;
注意:
(1)定義命令別名時,定義在全局文件“/etc/profile”或“/etc/bashrc”還是定義在用戶個人文件“~/.bashrc_profile”或“~/.bashrc”文件中,要看我們自己的需要,如果是想要定義對所有的用戶有效的命令別名,則就定義在文件“/etc/profile”或“/etc/bashrc”中,如果是隻想對特定用戶有效,那麼就將命令別名定義在用戶本身的家目錄文件“~/.bashrc_profile”或“~/.bashrc”中。
(2)只用管理員用戶纔有權限定義全局的,普通用戶是沒有權限進行編輯的。
(3)當我們真正登錄一個shell時,shell有這麼多的配置文件,那麼我們的shell應該先讀取哪一個,後讀取哪一個文件?
舉例:我們在全局文件中,定義“name=jerry”,在個人配置文件中定義“name=tom”,那麼這個變量name,到底等於誰?是先讀取的生效,還是後讀取的生效?
解:
後讀的纔是最終生效,因此配置文件的讀取的次序纔是至關重要。
配置文件的讀取次序:
交互式登錄shell進程:
/etc/profile------->/etc/profile.d/*------->~/.bash_profile------>~/.bashrc----->/etc/bashrc
非交互式登錄shell進程:
~/.bashrc------>/etc/bashrc------->/etc/profile.d/*
(所以我們自己編輯的腳本在運行時,他的環境是取決於“~/.bashrc---->/etc/bashrc---->/etc/pprofile.d/*”這三個文件的,如果是登錄系統是,則就取決於“/etc/profile---->/etc/profile.d/*----->~/.bashrc_profile------>~/.bashrc------>/etc/bashrc”這五個文件的。此前我們也知道,在命令行中定義的,不會永久有效,但是在change文件中定義的他不會立即有效,在配置文件中定義的,只對下次新登錄的shell進程有效,對以往的老shell進程是無效的,)
命令行中定義的特性,例如變量和別名作用於當前的shell進程的生命週期,即立即生效
配置文件中定義的特性,只對隨後新啓動的shell進程有效;
讓通過配置文件定義的特性立即生效:
(1)通過命令行重複定義一次;
(2)讓shell進程重讀配置文件;
重讀配置文件,執行下面的命令:(點號就相當於source)
~]#source /PATH/FROM/CONF_FILE
或
~]#. /PATH/FROM/CONF_FILE(配置文件的路徑)
注意:定義別名,主要是定義在/etc/bashrc或~/.bashrc文件中。
需求:
如果我們希望每次用戶登錄成功以後,都會echo一下,顯示說“歡迎登錄系統,你已經進入監控區域,你的所有行爲都將被記錄,請注意您的言行”那麼應該怎麼實現呢?
分析:
要想讓用戶登錄以後,能執行命令,應該在bashrc的配置文件中定義還是在profile文件中定義?我們知道,profile命令的兩個功能:(1)用於定義環境變量;(2)運行命令或腳本;那麼我們應該在全局中寫還是在個人的家目錄中寫?如果要想讓所有的用戶登陸後都能顯示,那就全局寫,否則就在個人目錄下寫。
假設我們在全局下寫,我們應該寫在那呢?
profile類的文件有兩大類,(1)/etc/profile(2)/etc/profile.d/*,那麼我們要是在/etc/profile.d/目錄下自己建立一個目錄,以後管理也好管理,不用的時候直接刪除就行。
因此在“/etc/profile.d/”下創建一個文件名隨便起,但是要以“.sh”結尾的文件例如:
該文件叫“welcome.sh”
編輯的內容爲:
echo "welcome aboard....."
然後當我們再次登錄一個shell進程時,就會在shell剛開始前面出現我們編寫內容:
所以,如果我們想在用戶登錄時給一些提示信息,那麼我們就可以在“/etc/profile.d/”目錄下定義一個“.sh”結尾的文件,或者在“/etc/profile”文件中定義。
有些配置不會立即生效,但是對後來的新登錄的窗口有效。
我們還可以在這樣的配置文件中定義一些環境變量的。
舉例:
輸出當前系統下的JAVA環境所在,我們通常輸出一個環境變量叫JAVA_HOME
創建一個文件,vim /etc/profile.d/java.sh,(因爲輸出環境變量使用“export”或“declare -x”)然後在寫的文件內容爲:“export JAVA_HOME=/usr”這樣就定義了一個環境變量“JAVA_HOME”,但是這麼文件不會立即有效,需要重讀這個配置爲文件,使用命令點“.”或“source”重讀文件java.sh,然後執行一下命令“export”或“declare -x”即可顯示新定義的環境變量。
問題:
(1)如何定義對所有用戶都生效的命令別名,例如 lftps='lftp 10.1.0.1/pub'
(2)如何實現centos用戶登錄時,提示其已經登錄,並顯示當前系統時間(提示顯示當前系統時間,可以通過命令引用來實現,也可以直接寫在文件中)
(3)如何實現root用戶的PATH環境查詢多幾個查詢路徑