Linux shell 知識心得01 shell基礎 +變數
shell基礎
一 程式設計與程式語言
Shell是一門程式語言,作為學習shell的開始,需要事先搞明白:程式設計的目的是什麼?什麼是程式語言?什麼是程式設計?
程式設計的目的:
#計算機的發明,是為了用機器取代/解放人力,而程式設計的目的則是將人類的思想流程按照某種能夠被計算機識別的表達方式傳遞給計算機,從而達到讓計算機能夠像人腦/電腦一樣自動執行的效果。
什麼是程式語言?
#上面提及的能夠被計算機所識別的表達方式即程式語言,語言是溝通的介質,而程式語言是程式設計師與計算機溝通的介質。在程式設計的世界裡,計算機更像是人的奴隸,人類程式設計的目的就命令奴隸去工作。
什麼是程式設計?
#程式設計即程式設計師根據需求把自己的思想流程按照某種程式語言的語法風格編寫下來,產出的結果就是包含一堆字元的檔案。
#強調:程式在未執行前跟普通檔案無異,只有程式在執行時,檔案內所寫的字元才有特定的語法意義
什麼是程式
程式設計的結果就是程式,更準確點說,程式就是一系列程式碼檔案
什麼是程序
程式執行的過程就是程序,或者說是作業系統幹活的過程,即作業系統拿著硬體去執行程式的過程
程式設計的步驟?
1、先把自己想讓計算機做事的步驟想清楚
2、再用一種計算機可以聽懂的語言把做事的步驟翻譯下來
二 程式語言分類
程式設計的語言的發展經歷了
# 機器語言:站在計算機(奴隸)的角度,說計算機能聽懂的語言,那就是直接用二進位制程式設計,直接操作硬體; # 優點:執行效率最高 # 缺點:1、二進位制指令難以記憶,開發時極容易出錯 # 2、開發程式的複雜度高:即便是完成一個簡單的功能,需要用到的二進位制指令的條數都會非常多 # 組合語言:站在計算機(奴隸)的角度,簡寫的英文識別符號取代二進位制指令去編寫程式,本質仍然是直接操作硬體; # 優點:解決了機器語言的二進位制指令難以記憶的問題,執行效率還是高 # 缺點:開發程式的複雜度依然很高:因為組合語言就是用英文標籤對應原來的二進位制指令,好記歸好記,開發 # 的複雜度卻沒有降低 ps:因為上述兩類語言都是在直接與計算機硬體打交道,離計算機硬體比較近,所以又統稱為低階語言 # 高階語言:站在人(奴隸主)的角度,說人話,即用人類的字元去編寫程式,遮蔽了硬體操作 # 優點:開發複雜度地,即開發效率高 # 缺點:速度肯定是不如低階語言,一直到今天,對速度要求極高的場景還會用到低階語言,比如作業系統的 # 排程程式
高階語言更貼近人類語言,因而造成了:它必須被翻譯成計算機能讀懂二進位制後,才能被執行,按照翻譯方式分為
# 1. 編譯型(需要編譯器,相當於用谷歌翻譯):如C,執行速度快,除錯麻煩
# 2. 解釋型(需要直譯器,相當於同聲傳譯):如python,執行速度慢,除錯方便
機器語言、組合語言、高階語言示例:https://www.cnblogs.com/linhaifeng/articles/7133167.html#_label2
三 計算機完整體系結構圖
瞭解玩程式語言後,我們得知我們使用程式語言的目的在於與計算機對話,準確地說是命令計算機幫我們做事情,但程式語言又分類很多種,不同種類有不同的特點和難度,這對於學習者有不同的要求,比如對機器語言,要求學習者必須具備非常深厚的計算機硬體知識才可以使用它編寫程式,一般等級比較低的人是沒辦法學會機器語言的!但我們大傢伙雖然比較low,但我們也都想用一下計算機硬體,家裡擺著一臺機器能夠幫我們做事情,就跟家裡養了一個奴隸一樣,誰不想呢?
所以,為了方便不同的人群都能夠操作計算機硬體,有了一層一層封裝的概念,如下圖所示
四 shell介紹
1、shell有兩層意思:
# 1、一層指的是shell這門語言,是一種特定的語法風格,規範等# 2、另外一層指的是專門用於解釋執行shell這門語言語法的應用程式,即shell直譯器,我們常用的是bash
linux系統上自帶多種shell直譯器,無需安裝
[root@localhost ~]# chsh -l/bin/sh/bin/bash/usr/bin/sh/usr/bin/bash/bin/tcsh/bin/csh
通常使用者登入成功後執行的shell程式為:/bin/bash
[root@localhost ~]# head -1 /etc/passwdroot:x:0:0:root:/root:/bin/bash
shell本身就是一門解釋型、弱型別、動態語言,與python相對應,Python屬於解釋型、強型別、動態語言,我們平時登入成功一個使用者後進入的就是bash直譯器的互動式環境,我們敲的命令其實都屬於shell這門語言的語法
2、為何要用shell
那還用說嗎,一些日常的運維工作自動化、配合計劃任務威力無窮,徹底解放雙手,你說它不香嗎?例如
- 自動備份
- 自動部署
- 監控指令碼
3、Shell VS python
shell語言
Shell 指令碼的優勢在於處理偏作業系統底層的業務,例如,Linux 內部的很多應用(有的是應用的一部分)都是使用 Shell 指令碼開發的,因為有 1000 多個 Linux 系統命令為它作支撐,特別是 Linux 正則表示式以及三劍客 grep、awk、sed 等命令。對於一些常見的系統指令碼,使用 Shell 開發會更簡單、更快速,例如,讓軟體一鍵自動化安裝、優化,監控報警指令碼,軟體啟動指令碼,日誌分析指令碼等,雖然 Python 也能做到這些,但是考慮到掌握難度、開發效率、開發習慣等因素,它們可能就不如 Shell 指令碼流行以及有優勢了。對於一些常見的業務應用,使用 Shell 更符合 Linux 運維簡單、易用、高效的三大原則。
python語言
Python 是近幾年非常流行的語言,它不但可以用於指令碼程式開發,也可以實現 Web 程式開發(知乎、豆瓣、YouTube、Instagram 都是用 Python 開發),甚至還可以實現軟體的開發(大名鼎鼎的 OpenStack、SaltStack 都是 Python 語言開發)、遊戲開發、大資料開發、移動端開發。現在越來越多的公司要求運維人員會 Python 自動化開發,Python 也成了運維人員必備的技能,每一個運維人員在熟悉了 Shell 之後,都應該再學習 Python 語言。Python 語言的優勢在於開發複雜的運維軟體、Web 頁面的管理工具和 Web 業務的開發(例如 CMDB 自動化運維平臺、跳板機、批量管理軟體 SaltStack、雲端計算OpenStack 軟體)等。
五 第一個shell程式
5.1 編寫shell程式的兩種環境
我們可以在兩個地方編寫shell程式
# 1、互動式環境除錯方便,無法永久儲存程式碼ps:互動式環境,請看附錄《shell直譯器互動式環境》,或者點選連結https://www.cnblogs.com/linhaifeng/articles/13976243.html# 2、寫到檔案中我們採用解釋型語言編寫的程式碼檔案通常都會被稱之為指令碼程式(使用nodpad++演示):可以永久儲存程式碼
5.2 編寫shell指令碼程式
其實我們在互動式環境裡敲的命令直接放到一個文字檔案裡,一個簡單的shell程式就完成了
強調:shell直譯器執行程式是解釋執行,即開啟檔案讀內容,因此檔案的字尾名沒有硬性限制,但通常定義為.sh結尾
[root@egon ~~]# mkdir -p /a/b[root@egon ~~]# vim /a/b/hello.sh[root@egon ~~]# cat /a/b/hello.sh#!/bin/bash#第一個shell小程式echo "hello world!"
解釋
-
1、第一行表示我們選擇使用的shell直譯器是bash,也可以用 :#!/usr/bin/env bash
shell的第一行比較特殊,一般都會以#!開始來指定使用的shell解釋的型別。在linux中,除了bash shell以外,還有很多版本的shell, 例如zsh、dash等等...不過bash shell還是我們使用最多的。
-
2、第二行以#符號開始,表示本行是註釋,註釋是對程式碼的解釋說明,註釋的內容不會執行,對關鍵程式碼加註釋是一種好的程式設計習慣
-
3、第三行中的echo是linux中的輸出命令,該行的意思很明顯的就是輸出hello world!
精通各種語言的hello world!看一看還是高階語言的簡潔
#C++#include <iostream> int main(void) { std::cout<<"Hello world"; }#C#include <stdio.h>int main(void){printf("\nhello world!");return 0;}#JAVApublic class HelloWorld{ // 程式的入口 public static void main(String args[]){ // 向控制檯輸出資訊 System.out.println("Hello World!"); }}#PHP<?php echo "hello world!"; ?>#Ruby日本人開發的,砸日本車的時候,順手就把你拖出去打死了,祭天puts "Hello world."#GOpackage mainimport "fmt"func main(){ fmt.Printf("Hello World!\n God Bless You!");}
5.3 執行shell指令碼程式
編寫好shell指令碼之後,執行它有幾種方式
-
方式一:絕對路徑,此時採用的是檔案頭指定的直譯器來解釋執行檔案內程式碼
# 許可權:# 1、對沿途資料夾有x許可權# 2、對目標檔案有r和x許可權# 例[root@egon ~~]# ll -d /ad--------x. 3 root root 15 11月 15 11:05 /a[root@egon ~~]# ll -d /a/bd--------x. 2 root root 18 11月 15 11:06 /a/b[root@egon ~~]# ll /a/b/hello.sh-rw-r--r-x. 1 root root 10 11月 15 11:06 /a/b/hello.sh[root@egon ~~]# su - egon[egon@egon ~~]$ /a/b/hello.shhello world![egon@egon ~~]$
-
方式二:相對路徑,需要加./作為字首,此時採用的仍是檔案頭指定的直譯器來解釋執行檔案內程式碼
# 許可權:# 1、對沿途資料夾有x許可權# 2、對目標檔案有r和x許可權# 例[root@egon ~~]# ll -d /ad--------x. 3 root root 15 11月 15 11:05 /a[root@egon ~~]# ll -d /a/bd--------x. 2 root root 18 11月 15 11:06 /a/b[root@egon ~~]# ll /a/b/hello.sh-rw-r--r-x. 1 root root 10 11月 15 11:06 /a/b/hello.sh[root@egon ~~]# su - egon[egon@egon ~~]$ /a/b/hello.sh # 絕對路徑的方式hello world![egon@egon ~~]$ cd /a[egon@egon /a/a]$ ./b/hello.sh # 相對路徑的方式,加./作為字首hello world![egon@egon /a/a]$
-
方式三:直譯器+檔案路徑(絕對路徑或相對路徑都可以),此時採用的是我們自己指定的直譯器來解釋執行檔案內程式碼
# 許可權:# 1、對沿途資料夾有x許可權# 2、對目標檔案有r許可權即可因為我們執行的是直譯器,當前使用者對解釋有執行許可權就可以了,這個許可權預設就有,而直譯器需要讀檔案內容來執行,所以需要對目標檔案有r許可權# 例[root@egon ~~]# chmod -R o=x /a[root@egon ~~]# chmod o=r /a/b/hello.sh[root@egon ~~]# su - egon[egon@egon ~~]$ cd /a[egon@egon /a/a]$ bash b/hello.shhello world![egon@egon /a/a]$
-
方式四:上述三種方式都是在子shell程序中執行程式,而方式四則是在當前shell程序中執行
# 許可權:# 1、對沿途資料夾有x許可權# 2、對目標檔案有r許可權即可上述三種方式都是在當前shell環境下開啟了一個新的shell直譯器環境/子shell來執行程式,指令碼程式在子shell中執行完畢後,子shell環境隨即關閉,然後返回到父級shell即當前shell環境中,如果就想在當前shell環境中執行,需要這麼做# 例如[egon@localhost shell]$ cd /home/egon/shell/[egon@localhost shell]$ ll hello.sh # 當前使用者egon對該檔案沒有執行許可權-rw-r--r--. 1 root root 60 8月 14 18:33 hello.sh # 下面兩種方式都一樣[root@egon ~~]# chmod -R o=x /a[root@egon ~~]# chmod o=r /a/b/hello.sh[root@egon ~~]# su - egon[egon@egon ~~]$ . /a/b/hello.sh # . 後跟空格,然後再跟絕對路徑hello world![egon@egon ~~]$ cd /a/b/[egon@egon /a/b/a/b]$ . hello.sh # . 後跟空格,然後再跟相對路徑hello world![egon@egon /a/b/a/b]$ source hello.sh # 跟上述方式一樣hello world!
在當前shell直譯器程序中執行指令碼與在子shell程序中執行指令碼的區別在於作用域,後續我們會詳細介紹
# 一個shell環境就是一個單獨的全域性作用域,不同的shell環境,無法訪問彼此shell環境中的變數[root@egon ~~]# su - egon[egon@localhost ~]$ x=111[egon@localhost ~]$ cat /a/b/hello.sh #!/bin/bashecho "hello world!" echo $x # 我們在這裡訪問一下全域性變數x[egon@localhost ~]$ source /a/b/hello.sh # 在當前shell環境執行,可以訪問到xhello world!111 # 取到了x的值[egon@localhost ~]$ bash /a/b/hello.sh # 在子shell環境執行,不能訪問到x hello world! # 此處列印空
5.4 除錯shell程式
除錯方式1:以除錯的方式執行
[root@egon test]# sh -vx login.sh # 不加-v選項,只會顯示程式中執行的程式碼,不會顯示註釋資訊
除錯方式2:只調試語法是否有問題,比如if判斷少了fi結尾
[root@egon test]# sh -n login.sh
除錯方式3:僅除錯指令碼的一部分,用set -x與set +x包含,執行過程中會只打印它們包含的程式碼段的執行情況
[root@egon test]# cat login.sh #!/usr/bin/env bashset -xread -p "請輸入您的名字: " nameread -p "請輸入您的密碼: " pwdset +xif [[ "$name" == "egon" && "$pwd" == "123" ]];then echo "登入成功"else echo "賬號或密碼錯誤"fi[root@egon test]# . login.sh
5.5 註釋是程式碼之母
井號#開頭的內容不會執行,註釋的作用有二
-
1、註釋掉暫時不想執行的程式碼
-
2、為關鍵程式碼添加註解說明,增強程式的可讀性和可維護性
1. 可以加在被註釋程式碼的正上方單獨一行,或者被註釋程式碼的正後方,例如# 註釋。。。echo "hello" # 註釋。。。2. 不用全部加註釋,只需要在自己覺得重要或不好理解的部分加註釋即可3. 註釋可以用中文或英文,但不要用拼音
六 shell編寫規範與建議
一 指令碼編寫規範
-
1、指令碼存放目錄需要統一
-
2、shell指令碼的結尾以.sh
-
3、指令碼開頭要有直譯器如#!/bin/bash 或者 #!/usr/bin/env bash
-
4、指令碼開頭注意加時間、作者、聯絡郵箱、指令碼作用等資訊
# Author egon 2020-8-30 version 1 des:xxxxx
-
5、關鍵程式碼應該用#號加上註釋
二 程式碼編寫好習慣
-
1、成對的符號儘量一次性寫出來,防止遺漏
例如大括號{},中括號[],小括號(),單引號’’,雙引號””,反引號``等
-
2、括號的保留空格習慣
中括號[ ]兩端需要留有空格,不然會報錯。書寫時即可留出空格然後書寫內容。如果不知道大括號{},中括號[],小括號(),到底哪種括號需要兩端留空格,可以在書寫這些括號的時候兩端都保留空格來進行書寫,這樣可以有效避免因空格導致的各種錯誤。
-
3、流程控制語句一次性書寫完再新增內容
例1:if語句格式一次書寫完成if 條件內容then 條件成立後執行的程式碼fi 例2:for迴圈格式一次書寫完成for條件內容 do 條件成立後執行的程式碼 done提示:while、until、case等語句也是一樣
-
4、程式碼縮排提高可讀性
變數
一 變數介紹
什麼是變數?
量指的是記錄事物的狀態
變指的是事物的狀態是可以發生變化的
變數本質就是一種資料存取的機制,變數的資料都是存放於記憶體中的
為何要有變數?
程式執行的本質就是一系列狀態的變化,
程式=資料+功能,
程式執行的本質就是一系列狀態的變化,變是程式執行的直接體現,所以我們需要有一種機制能夠反映或者說是儲存下來程式執行時狀態以及狀態的變化。
# 比如:
英雄的等級為1,打怪升級(變)為10
殭屍的存活狀態True,被植物打死了,於是變為False
人的名字為egon,也可以修改為Egon
二 變數的使用
2.1 先定義、後引用
先定義
# 1、語法:變數名=值
# 2、注意:等號左右兩邊不能有空格!!!
# 3、例如:
[root@localhost shell]# name="egon"
後引用
[root@localhost shell]# ip="192.168.11.10"
[root@localhost shell]# echo $ip
192.168.11.10
注意:如果是列印百分比,建議使用${變數名}%
[root@localhost shell]# percent=33
[root@localhost shell]# echo ${percent}%
33%
2.2 刪除變數
[root@localhost shell]# x=111
[root@localhost shell]# unset x
[root@localhost shell]# echo $x
[root@localhost shell]#
三 變數的三大組成部分
基於上一小節我們得知,定義一個變數由三大部分組成
-
1、變數名:用來訪問到變數值的
-
2、賦值符號:將變數值的記憶體地址繫結給變數名
-
3、變數值:即我們存的資料
3.1 變數名的命名規範
定義變數把資料存到記憶體中不是目的,目的是以後要取出來用,而變數名就是用來取變數值的,而變數的名字直接表明了所存值的意義,可見變數名的命名非常重要
# 變數名的命令應該見名知意,同時遵循如下規則
以字母或下劃線開頭,剩下的部分可以是:字母、數字、下劃線,最好遵循下述規範:
1.以字母開頭
2.使用中劃線或者下劃線做單詞的連線
3.同類型的用數字區分
4.對於檔名的命名最好在末尾加上拓展名
例如: sql_bak.tar.gz,log_bak.tar.bz2
5、不要帶有空格、?、*等特殊字元
6、不能使用bash中的關鍵字,例如if,for,while,do等
7、不要和系統環境變數衝突
3.2 變數值的三種來源
(1)直接賦值
# 1. 顯式賦值:變數名=變數值
示例:
ip1=192.168.11.200
school="Shanghai oldboy"
today1=`date +%F`
today2=$(date +%F)
# 2、應用示例
[root@localhost ~]# url="www.baidu.com"
[root@localhost ~]# echo $url
www.baidu.com
[root@localhost ~]# url="www.sina.com.cn"
[root@localhost ~]# echo $url
www.sina.com.cn
(2)從位置引數獲取變數值
從呼叫指令碼時傳入的位置引數獲取變數值:./b.sh a1 a2 a3
需要用到$n獲取第n個位置引數值,超過10需要用${n},如下
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
# 示例
[root@egon ~]# cat b.sh
#!/usr/bin/env bash
echo ${0}
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8
echo $9
echo ${10}
echo ${11}
echo ${12}
# 執行
[root@egon ~]# chmod +x b.sh
[root@egon ~]# ./b.sh a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
./b.sh
a1
a2
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12
# 企業使用:可以實現指令碼的不同功能(服務程式管理指令碼-啟動功能 關閉功能 重啟功能)
server.sh start|stop|restart
(3)與使用者互動獲取值
何為互動,即輸入、輸出
# 一:read接收使用者的輸入,即從鍵盤讀入變數值
read 變數名
read -p "提示資訊: " 變數名
read -t 5 -p "提示資訊: " 變數名 # -t指定秒數
read -n 2 變數名 # -n讀取的字元個數
=======》應用示例:vim first.sh
back_dir1=/var/backup
read -p "請輸入你的備份目錄: " back_dir2
echo $back_dir1
echo $back_dir2
企業使用:可以根據需求調整變數值,可以便於新員工快速掌握企業指令碼使用
# 二:輸出
# 2.1 echo命令,詳解情況:https://www.cnblogs.com/linhaifeng/articles/13976349.html
[root@egon ~~]# name="egon"
[root@egon ~~]# age=18
[root@egon ~~]# echo -e "my name is $name my age is $age"
my name is egon my age is 18
還可以輸出帶顏色(瞭解即可)
echo -e "\033[31m 紅色字 \033[0m"
echo -e "\033[34m 黃色字 \033[0m"
echo -e "\033[41;33m 紅底黃字 \033[0m"
echo -e "\033[41;37m 紅底白字 \033[0m"
# 2.2 prinf命令(C語言風格)
%s
%d
%7.3f 列印浮點數,總寬度為7,小數位保留3位,並且四捨五入
[root@egon ~~]# name="egon"
[root@egon ~~]# age=18
[root@egon ~~]# printf "my name is %s my age is %s\n" $name $age
my name is egon my age is 18
[root@egon ~]# salary=3.3339
[root@egon ~]# printf "my name is %s my age is %7.3f\n" $name $salary
my name is egon my age is 3.334
三 預定變數
$* 所有的引數$@ 所有的引數$# 引數的個數$$ 當前程序的PID # 此外,可以使用只讀變數來獲取父程序的PID:$PPID、獲取執行指令碼的使用者ID:$UID$? 上一個命令的返回值 0表示成功 示例1:[root@egon ~]# chmod +x b.sh [root@egon ~]# ./b.sh a1 a2 a3 a4 a5a1 a2 a3 a4 a5a1 a2 a3 a4 a55189881[root@egon ~]# cat b.sh #!/usr/bin/env bashecho $*echo $@echo $#echo $$ping -c1 192.168.11.10 &>/dev/nullecho $?示例2:[root@egon ~]# vim ping.sh#!/bin/bash ping -c2 $1 &>/dev/null if [ $? = 0 ];then echo "host $1 is ok" else echo "host $1 is fail" fi[root@egon ~]# chmod +x ping.sh[root@egon ~]# ./ping.sh 192.168.11.10
瞭解:
如果我們想從命令列中獲取指令碼呼叫者傳入的引數值,用$n可以取到,但如果指令碼呼叫者在命令列傳入的引數個數不固定,那麼此時就需要用$*或$@來獲取了$*與$@獲取的是所有位置引數,$0除外
當指令碼呼叫者的傳參形式如下是
[root@localhost ~]# ./script.sh 命令1 命令2 命令3
針對for迴圈語句:for i in 元素,for迴圈會按照空格作為分隔符來一個個取元素,所以此時$*
與$@
並無區別
[root@localhost ~]# cat script.sh #!/bin/bashfor i in $*do echo $idoneecho "=================="for i in $@do echo $idone[root@localhost ~]# [root@localhost ~]# ./script.sh 命令1 命令2 命令3命令1命令2命令3==================命令1命令2命令3
當指令碼呼叫者的傳參形式如下時
[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 引數"
針對for迴圈語句:for i in 元素,for迴圈會按照空格作為分隔符來一個個取元素,所以此時$*
與$@
如果不加引號,第三個命令:"命令3 引數",為被以空格為分隔符識別成兩部分
[root@localhost ~]# cat script.sh #!/bin/bashfor i in $*do echo $idoneecho "=================="for i in $@do echo $idone[root@localhost ~]# [root@localhost ~]# ./script.sh 命令1 命令2 "命令3 引數"命令1命令2命令3引數==================命令1命令2命令3引數
所以需要為$*
與$@
加上引號
[root@localhost ~]# cat script.sh #!/bin/bashfor i in "$*"do echo $idoneecho "=================="for i in "$@"do echo $idone[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 引數"命令1 命令2 命令3 引數==================命令1命令2命令3 引數[root@localhost ~]#
但此時為$*
就會把所有位置引數識別成一個整體,所以總結如下:
當指令碼呼叫者的傳參形式如下時[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 引數"需要使用"$@"來分別獲取一個個完整的命令for i in "$@"do echo $idone其餘情況$*與$@完全一致
四 常量
相對於變數,常量就是不可以被改變的量,即只能讀不能改,所以又稱之為只讀變數
使用 readonly 命令可以將變數定義為只讀變數,只讀變數的值不能被改變。[root@egon ~]# x=111[root@egon ~]# readonly x[root@egon ~]# x=666-bash: x: 只讀變數