shell在linux里摇摇晃晃
1.shell不只是一種解釋器,還是一種編程工具
查看系統中可用的shell,linux默認使用 Bash Shell
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
redhat系將/bin/sh連接到bash上
[root@localhost ~]# ll /bin/sh
lrwxrwxrwx. 1 root root 4 May 17 08:32 /bin/sh -> bash
[root@localhost ~]# bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
2.shell的功能
當一臺系統運行起來時,內核會被調入內存中運行,由內核執行所有底層的工作,它會將所有應用程序及用戶的數據翻譯為CPU的基本指令,并將其送至處理器。為了對用戶屏蔽這些復雜的技術細節,同時也是為了保護內核不會因為用戶直接操作而受到損害,有必要在內核之上創建一個層,該層就是一個"殼",也就是shell。
工作模式:互動模式和腳本模式
3.運行腳本
第一種:直接在該腳本所在目錄中直接bash該腳本,腳本中的#!/bin/bash可以省略
第二種:給該腳本加上可執行權限,然后使用"./腳本名"來運行,沒有權限會報錯
第三種:把腳本的移動到$PATH的任意路徑下,,使其成為默認的系統命令
4.腳本排錯
加入echo或者echo結合sleep
借助-x參數來觀察腳本的運行情況:bash -x helloworld.sh
為了更精細地調試運行shell,我們可以借助第三方工具bashdb。這是一個類似于GDB的腳本調試軟件,小巧而強大,具有設置斷點、單步執行、觀察變量等功能。
5.shell的內建命令
就是bash自身提供的命令,而不是文件系統中的某個可執行文件。比如人類的語言溝通就是與生俱來的能力,而移動電話只是一個用來溝通的外部工具。
通常來說,內建命令會比外部命令執行得更快,執行外部命令時不但會觸發磁盤I/O,還需要fork出一個單獨的進程來執行,執行完后再退出。而執行內建命令相當于調用當前shell進程的一個函數。
判斷一個命令是否為外部命令
cd是一個內建命令
[root@localhost ~]# type cd
cd is a shell builtin
ifconfig是一個外部命令
[root@localhost ~]# type ifconfig
ifconfig is /usr/sbin/ifconfig
6.執行程序
"."(點號)
點號用于執行某個腳本,甚至腳本沒有可執行權限也可以運行,如果你不想修改某個測試腳本時,可以這樣用。
[root@localhost ~]# ll helloworld.sh
-rw-r--r-- 1 root root 31 Jul 16 11:04 helloworld.sh
[root@localhost ~]# ./helloworld.sh
bash: ./helloworld.sh: Permission denied
沒有權限也不會報錯
[root@localhost ~]# . ./helloworld.sh
hello world
source與點號類似,source命令也可以讀取并在當前環境中執行腳本,同時還可返回腳本中最后一個命令的返回狀態;如果沒有返回值則返回0,代表執行成功;如果未找到指定的腳本則返回false
7.別名
alias可用于創建命令的別名,若直接輸入該命令且不帶任何參數,則列出當前用戶使用了別名的命令。
自定義別名,比如說一般的關機命令是shutdown -h now,寫起來比較長,這時可以重新定義一個自己專屬的關機命令
[root@localhost ~]# alias myshutdown='shutdown -h now'
不過這種方式在重新登陸之后會失效,如果想要用就生效,需要將該配置寫在用戶家目錄中的.bashrc文件中。
[root@localhost ~]# less .bashrc
.bashrc
User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
刪除別名
unalias,該命令用于刪除當前shell環境中的別名
第一種刪除:unalias 某個命令的別名
第二種刪除:unalias -a 這個刪除當前shell環境中所有的別名
8.任務前后臺切換:bg、fg、jobs
該命令用于將任務放置后臺運行,一般會與ctrl+z、fg、&符號聯合使用。典型的使用場景是運行比較耗時的任務。比如打包某個占用較大空間的目錄,若在前臺執行,那么在它完成之前會一直占用當前的終端,而導致無法執行其他任務,此時就應該將這類任務放置后臺
ctrl+z:組合鍵用于暫停前臺任務
jobs:查看暫停的任務
&:將任務放置后臺運行,如果預知某個任務耗時很久,可以一開始就將命令放入后臺運行
fg 1:將后臺編號為1的任務放到當前運行
9.聲明變量:declare、typeset
這兩個命令的作用是用來生命變量的
declare -i聲明整型變量
[root@localhost ~]# declare -i num1=1
declare -r 聲明變量為只讀
[root@localhost ~]# declare -r num2=2
[root@localhost ~]# num2=3
bash: num2: readonly variable
10.echo
默認情況下,echo會隱藏-e參數(禁止解釋打印反斜杠轉義的字符)。比如"\n"代表換行,使用echo打印"\n"時,只會把"\n"當作普通字符處理。如果要打印轉義字符,則需要通過使用-e參數來允許。
[root@localhost ~]# echo '\n'
\n
[root@localhost ~]# echo -e '\n'
[root@localhost ~]#
11.跳出循環:break
從一個循環(for、while、until、select)中退出。break后可以跟一個數字n,代表跳出n層循環,n必須大于1,如果n比當前循環層數還要大,則跳出所有循環。
12.循環控制:continue
停止當前循環,并執行外層循環(for、while、until、select)的下一次循環。continue后可以跟一個數字n,代表跳至外部第n層循環,n必須大于1,如果n比當前循環層數還要大,則跳至最外層的循環。
13.eval
將所跟參數作為shell的輸入,并執行產生的命令
用法例一:將字符串解析成命令執行
[root@localhost shell_learning]# cmd="ls -l /etc/passwd"
[root@localhost shell_learning]# eval $cmd
-rw-r--r-- 1 root root 2857 Jun 11 14:52 /etc/passwd
用法例二:程序運行中根據某個變量確定實際的變量名
[root@localhost shell_learning]# name1=john
[root@localhost shell_learning]# name2=wang
[root@localhost shell_learning]# num=1
[root@localhost shell_learning]# eval echo "$"name$num
john
用法例三:將某個變量的值當作另一個變量名并給其賦值
[root@localhost shell_learning]# name1=john
[root@localhost shell_learning]# name2=wang
[root@localhost shell_learning]# eval $name1="$name2"
[root@localhost shell_learning]# echo $john
wang
14.執行命令來取代當前的shell:exec
內建命令exec并不啟動新的shell,而是用要被執行的命令替換當前的shell進程,并且將老進程的環境清理掉,而且exec命令后的其他命令將不再執行。假設在一個shell里面執行exec echo "hello"命令,那么打印hello之后終端就會斷開(xshell)或者terminal直接退出(linux中的偽終端)。
為了避免上述情況,一般將exec命令放到一個shell腳本里面,由主腳本調用這個腳本,主腳本在調用子腳本執行時,當執行到exec后,該子腳本進程就被替換成相應的exec的命令。exec的典型用法是與find聯合使用,用find找出符合匹配的文件,然后交給exec處理。
[root@localhost ~]# find / -name ".conf" -exec ls -l {} ;
{}與\之間要有空格
15.退出shell:exit
在shell腳本中使用exit代表退出當前腳本,該命令可以接受的參數是一個狀態值n,代表退出的狀態,下面的腳本什么都不會做,一旦運行就以狀態值為5退出。如果不指定,默認狀態值是0。
[root@localhost ~]# vim exit.sh
[root@localhost ~]# sh exit.sh
[root@localhost ~]# echo $?
5
16.使變量能被子shell識別:export
用戶登陸到系統之后,系統將啟動一個shell,用戶可以在該shell中聲明變量,也可以創建并運行shell腳本。在父shell中創建的變量時,這些變量并不會被其他子shell進程所知,也就是說變量默認情況下是"私有"的,或稱"局部變量"。使用export命令可以將變量導出,使得該shell的子shell都可以使用該變量,這個過程稱為變量輸出。
[root@localhost ~]# cat export.sh
!/bin/bash
echo $var
[root@localhost ~]# var=100
[root@localhost ~]# echo $var
100
[root@localhost ~]# sh export.sh
export之后,子shell就能訪問父shell里面$var的值了
[root@localhost ~]# export var=100
[root@localhost ~]# echo $var
100
[root@localhost ~]# sh export.sh
100
需要注意的一點,子shell讀取到父shell中變量var的值,但這只是值的傳遞。如果在子shell中嘗試修改變量var的值,改變的只是var在子shell中的值,并不會影響父shell中var的值
17.整數運算
[root@localhost ~]# let i=2+2
[root@localhost ~]# echo $i
4
求餘
[root@localhost ~]# let M=15%7
[root@localhost ~]# echo $M
1
次方
[root@localhost ~]# let N=2**3
[root@localhost ~]# echo $N
8
自加一
[root@localhost ~]# i=1
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
2
自加十
[root@localhost ~]# echo $i
2
[root@localhost ~]# let i+=10
[root@localhost ~]# echo $i
12
自乘十
[root@localhost ~]# let i*=2
[root@localhost ~]# echo $i
24
自除六
[root@localhost ~]# let i/=6
[root@localhost ~]# echo $i
4
18.聲明局部變量:local
用於在腳本內定義局部變量,典型的用法是用於函數體內,其作用域也在聲明該變量的函數體中。如果試圖在函數外使用local聲明變量,則會提示錯誤
19.從標準輸入讀取一行到變量:read
有時候我們開發的腳本必須具有交互性,也就是在運行過程依賴人工輸入才能繼續。
[root@localhost shell_learning]# cat read.sh
!/bin/bash
declare N
echo "12 bottles of beer in a box"
echo -n "How many box do you want:"
read N
echo "$((N*12)) bottle in total"
[root@localhost shell_learning]# sh read.sh
12 bottles of beer in a box
How many box do you want3
36bottle in total
優化
!/bin/bash
echo "12 bottles of beer in a box"
read -p "How many box do you want:" N
echo "$((N*12)) bottle in total"
20.定義函數返回值:return
常見的用法是返回一個數字return n,如果沒有指定n值,則返回狀態是函數體中執行的最后一個命令的退出狀態
[root@localhost ~]# cat return.sh
!/bin/bash
function fun_01 {
return 1
}
fun_01
echo $?
[root@localhost ~]# bash return.sh
1
21.向左移動位置參數:shift
一個腳本在運行時可以接收,從左到有第一個參數被記作$1,第二個參數為$2,以此類推。所有參數記作$@或$*
參數總個數記為$#,腳本本身記作$0
shift命令可以對腳本的參數做"偏移"操作。假設腳本有A B C這三個參數,那么$1為A,$2為B,$3為C;shift一次
之后$1為B,$2為C。再shift一次,$1為C
22.顯示并設置進程資源限度:ulimit
軟限制不能高于硬限制
查看系統軟限制
[root@localhost ~]# ulimit -a
core file size (blocks, -c) 0 文件大小
data seg size (kbytes, -d) unlimited 數據段大小
scheduling priority (-e) 0 調度優先級
file size (blocks, -f) unlimited 創建文件的大小
pending signals (-i) 63121 掛起的信號數量
max locked memory (kbytes, -l) 64 最大鎖定內存的值
max memory size (kbytes, -m) unlimited 最大可用的常駐內存值
open files (-n) 1024 最大的文件打開數
pipe size (512 bytes, -p) 8 管道最大緩沖區的值
POSIX message queues (bytes, -q) 819200 消息隊列的最大值
real-time priority (-r) 0 程序的實時性優先級
stack size (kbytes, -s) 8192 棧大小
cpu time (seconds, -t) unlimited 最大cpu占用時間
max user processes (-u) 63121 用戶最大進程數
virtual memory (kbytes, -v) unlimited 最大虛擬內存
file locks (-x) unlimited 文件鎖
設置最大文件打開數(命令行設置,在系統重啟后就失效)
同時設置軟限制和硬限制
[root@localhost ~]# ulimit -n 4096
僅設置軟連接
[root@localhost ~]# ulimit -S -n 4096
僅設置硬連接
[root@localhost ~]# ulimit -H -n 4096
修改配置文件(永久生效)
格式:
可用item
can be one of the following:
- core - limits the core file size (KB)
- data - max data size (KB)
- fsize - maximum filesize (KB)
- memlock - max locked-in-memory address space (KB)
- nofile - max number of open file descriptors 最大文件打開數
- rss - max resident set size (KB) 最大常駐內存值
- stack - max stack size (KB)
- cpu - max CPU time (MIN)
- nproc - max number of processes 最大進程數
- as - address space limit (KB) 虛擬地址空間
- maxlogins - max number of logins for this user
- maxsyslogins - max number of logins on the system
- priority - the priority to run user process with
- locks - max number of file locks the user can hold
- sigpending - max number of pending signals
- msgqueue - max memory used by POSIX message queues (bytes)
- nice - max nice priority allowed to raise to values: [-20, 19]
- rtprio - max realtime priority
23.測試表達式:test
根據測試結果返回0(測試失敗)或1(測試成功)
查看系統中可用的shell
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
修改用戶登陸的shell
chsh 用戶名
[root@localhost ~]# chsh liangjiongyao
Changing shell for liangjiongyao.
New shell [/bin/bash]: /bin/sh
Shell changed.
不加用戶名默認就是root
其實修改的是/etc/passwd里面的最后一列
liangjiongyao?1000:1000:liangjiongyao:/home/liangjiongyao:/bin/sh
變量賦值和取值
注意點:變量名和變量值之間用等號緊緊相連,之間沒有任何空格--skip-broken
[root@localhost ~]# name=john
[root@localhost ~]# echo $name
john
[root@localhost ~]# name="joe"
[root@localhost ~]# echo $name
joe
當變量有空格時必須用引號括起,否則會報錯
[root@localhost ~]# name=john wang
bash: wang: command not found...
[root@localhost ~]# name="john wang"
[root@localhost ~]# echo $name
john wang
注意點:獲取變量值的一種比較保險的方式
[root@localhost ~]# name="john wang"
[root@localhost ~]# echo ${name}
john wang
[root@localhost ~]# name="sue"
[root@localhost ~]# echo $namehello #變量namehello沒有定義
[root@localhost ~]# echo ${name}hello
suehello
注意點:如果變量值引用的是其他變量,則必須使用雙引號。因為單引號會阻止shell解釋變量
[root@localhost ~]# name="john"
[root@localhost ~]# name1="$name"
[root@localhost ~]# echo $name1
john
[root@localhost ~]# name2='$name'
[root@localhost ~]# echo $name2
$name
因為shell具有弱變量的特性,因此即便在沒有預先聲明變量的好時候也是可以引用的。如果在腳本中引用沒有定義的
變量,導致腳本異常,那么排查起來也很困難。因此必須使腳本遵循"先聲明再使用"。
[root@localhost ~]# echo $undefinedVar
[root@localhost ~]# shopt -o -s nounset
[root@localhost ~]# echo $undefinedVar
bash: undefinedVar: unbound variable
取消變量
就是將變量從內存中釋放,使用的命令是unset,后面跟變量名,函數也是可以被取消的。
[root@localhost ~]# name=john
[root@localhost ~]# echo $name
john
[root@localhost ~]# unset name
[root@localhost ~]# echo $name
bash: name: unbound variable
取消函數
[root@localhost liangjiongyao]# cat test
!/bin/bash
unset_function(){
echo "Hello World"
}
unset unset_function
unset_function
[root@localhost liangjiongyao]# bash test
test: line 7: unset_function: command not found
命令的返回值
linux中規定正常退出的命令和腳本應該以0作為其返回值,任何非0的返回值都表示命令未正確退出或未正常執行
[root@localhost liangjiongyao]# ifco
bash: ifco: command not found...
[root@localhost liangjiongyao]# echo $?
127
[root@localhost liangjiongyao]# ping 1
connect: Invalid argument
[root@localhost liangjiongyao]# echo $?
2
數組
數組是一種特殊的數據結構,其中的每一項被稱為一個元素,對于每個元素,都可以用索引方式取出元素的值。shell中數組對元組個數沒有限制,
但只支持一維數組。
定義數組
[root@localhost liangjiongyao]# declare -a Array
[root@localhost liangjiongyao]# Array[0]=0
[root@localhost liangjiongyao]# Array[1]=1
[root@localhost liangjiongyao]# Array[2]="HelloWorld"
創建時賦值
[root@localhost liangjiongyao]# declare -a Name=('john''sue')
不使用declare關鍵字創建數組
[root@localhost liangjiongyao]# Name=('john''sue')
對特定的元素賦值
[root@localhost liangjiongyao]# Score=([3]=3 5=[5] [7]=7)
數組操作
取值:${數組名[索引]}
[root@localhost liangjiongyao]# echo ${Array[0]}
0
[root@localhost liangjiongyao]# echo ${Array[2]}
HelloWorld
[root@localhost liangjiongyao]# echo ${Array[1]}
1
一次性取出數組中所有元素的值
[root@localhost liangjiongyao]# echo ${Array[@]}
0 1 HelloWorld #以空格隔開的元素值
[root@localhost liangjiongyao]# echo ${Array[*]}
0 1 HelloWorld #一整個字符串
獲取數組的長度(即元素個數)
[root@localhost liangjiongyao]# echo ${#Array[*]}
3
[root@localhost liangjiongyao]# echo ${#Array[@]}
3
如果某個元素是字符串,還可以通過指定索引的方式獲得該元素的長度
[root@localhost liangjiongyao]# echo ${#Array[2]}
10
[root@localhost liangjiongyao]# echo ${Array[2]}
HelloWorld
取出數組的第一、第二個元素
[root@localhost liangjiongyao]# echo ${Array[@]:1:2}
1 HelloWorld
取出數組的第二個元素中的第0~5個字符
[root@localhost liangjiongyao]# echo ${Array[2]:0:5}
Hello
連接數組:將若干個數組進行拼接操作
[root@localhost liangjiongyao]# Conn=(${Array[@]} ${Name[@]})
[root@localhost liangjiongyao]# echo ${Conn[@]}
0 1 HelloWorld johnsue
替換元素:將數組內某個元素的值替換成其他值
[root@localhost liangjiongyao]# Array=(${Array[@]/HelloWorld/HelloJohn})
[root@localhost liangjiongyao]# echo ${Array[@]}
0 1 HelloJohn
取消數組或元素
取消數組中的一個元素
[root@localhost liangjiongyao]# unset Array[1]
[root@localhost liangjiongyao]# echo ${Array[@]}
0 HelloJohn
取消整個數組
[root@localhost liangjiongyao]# unset Array
[root@localhost liangjiongyao]# echo ${Array[@]}
本行為空
只讀變量
通過readonly內建命令創建的變量
[root@localhost liangjiongyao]# readonly ro=100
[root@localhost liangjiongyao]# ro=200
bash: ro: readonly variable
[root@localhost liangjiongyao]# declare -r rp=100
[root@localhost liangjiongyao]# rp=200
bash: rp: readonly variable
變量的作用域
又稱“名稱空間”,表示變量的上下文。相同的變量可以在多個命名空間中定義,并且彼此之間互不相干。
就像A班有個小明,B班也有個小明,雖然他們都叫小明但是由于所在的班級不一樣(對應于命名空間),所以不會造成混亂
2018-08-23 晚
控制字符
即ctrl+key組合鍵一起使用,用于修改終端或文本顯示,一般在命令行下使用,腳本中不能使用。
ctrl+L 清屏
ctrl+Z 暫停前臺作業
ctrl+U 刪除光標到行首的所有字符
測試結構
格式一:test expression是一個表達式,可以是算術比較、字符串比較、文本和文件屬性比較等
格式二:使用[ expression ] #推薦,易于與if、case、while這些條件判斷的關鍵字連用
[root@localhost yum.repos.d]# test -e /var/log/messages
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# test -e /var/log/messages01
[root@localhost yum.repos.d]# echo $?
1
[root@localhost yum.repos.d]# [ -e /var/log/messages ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ -e /var/log/messages01 ]
[root@localhost yum.repos.d]# echo $?
1
文件測試符
-b 當文件存在且是個塊文件時返回真,否則為假
-c 當文件存在且是個字符設備時返回真,否則為假
-d 當文件存在且是個目錄時返回真,否則為假
-e 當文件或者目錄存在時返回真,否則為假
-f 當文件存在且為普通文件時返回真,否則為假
-x 當文件存在且為可執行文件時返回真,否則為假
-s 當文件存在且大小不為0時返回真,否則為假
-S 當文件存在且為socket文件時返回真,否則為假
-r 當文件存在且為可讀文件時返回真,否則為假
-w 當文件存在且為可寫文件時返回真,否則為假
-x 當文件存在且為可執行文件時返回真,否則為假
FILE1 -nt FILE2 當FILE1比FILE2新時返回真,否則為假
FILE1 -ot FILE2 當FILE1比FILE2舊時返回真,否則為假
文件新舊的比較的主要使用場景是判斷文件是否被更新或增量備份時,用于判斷一段時間以來更新過的文件
字符串測試
-z "string" 字符串string為空時返回真,否則為假
-n "string" 字符串string非空時返回真,否則為假
"string1" = "string1" string1和string2相同時返回真,否則為假
"string1" != "string1" string1和string2不相同時返回真,否則為假
"string1" > "string2" 按照字典排序,字符串string1排在string2之前返回真,否則為假
"string1" < "string2" 按照字典排序,字符串string1排在string2之后返回真,否則為假
[root@localhost yum.repos.d]# str1=""
[root@localhost yum.repos.d]# str2="hello"
[root@localhost yum.repos.d]# [ "$str1" > "$str2" ] #大于號需要轉義
[root@localhost yum.repos.d]# echo $?
1
[root@localhost yum.repos.d]# [[ "$str1" > "$str2" ]] #不向轉義就用[[]]
[root@localhost yum.repos.d]# echo $?
1
整數測試
-eq 等于
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
-ne 不等于
[root@localhost yum.repos.d]# num1=10
[root@localhost yum.repos.d]# num2=10
[root@localhost yum.repos.d]# num3=9
[root@localhost yum.repos.d]# num4=11
[root@localhost yum.repos.d]# [ "$num1" -eq "$num2" ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ "$num1" -gt "$num3" ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ "$num1" -ge "$num2" ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ "$num1" -le "$num2" ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ "$num1" -ne "$num3" ]
[root@localhost yum.repos.d]# echo $?
0
邏輯測試符和邏輯運算符
邏輯測試用于連接多個測試條件,并返回整個表達式的值
邏輯非的使用
測試值為真的表達式在使用邏輯非后,表達式變為假,反之亦然
[root@localhost yum.repos.d]# [ ! -e /var/log/messages ]
[root@localhost yum.repos.d]# echo $?
1
邏輯與使用
表達式都為真,整個表達式才返回真,否則返回假
[root@localhost yum.repos.d]# [ -e /var/log/messages -a -e /var/log/messages ]
[root@localhost yum.repos.d]# echo $?
0
[root@localhost yum.repos.d]# [ -e /var/log/messages -a -e /var/log/messages01 ]
[root@localhost yum.repos.d]# echo $?
1
邏輯或的使用
測試表達式中只要有真,則整個表達式返回真
[root@localhost yum.repos.d]# [ -e /var/log/messages -o -e /var/log/messages01 ]
[root@localhost yum.repos.d]# echo $?
0
邏輯運算符
! 非
&& 與
|| 或
[root@localhost yum.repos.d]# ! [ -e /var/log/messages ]
[root@localhost yum.repos.d]# echo $?
1
[root@localhost yum.repos.d]# [ -e /var/log/messages ]&&[ -e /var/log/messages01 ]
[root@localhost yum.repos.d]# echo $?
1
[root@localhost yum.repos.d]# [ -e /var/log/messages ]||[ -e /var/log/messages01 ]
[root@localhost yum.repos.d]# echo $?
0
判斷
if判斷結構
if exp;then
command
......
fi
[root@localhost liangjiongyao]# cat score01.sh
!/bin/bash
echo -n "please input a score:"
read score
if [ "$score" -lt 60 ];then
echo "C"
fi
if [ "$score" -lt 80 -a "$score" -ge 60 ];then
echo "B"
fi
if [ "$score" -ge 80 ];then
echo "A"
fi
分支(if/else)
if exp;then
command
else
command
fi
[root@localhost liangjiongyao]# cat check_file.sh
!/bin/bash
FILE=/var/log/messages
if [ -e $FILE ];then
echo "$FILE exist"
else
echo "$FILE not exist"
fi
if/elif/else判斷結構
if exp1;then
command
elif exp2;then
command
else
command
fi
優化score01.sh
[root@localhost liangjiongyao]# cat score02.sh
!/bin/bash
echo -n "please input a score:"
read score
if [ "$score" -lt 60 ];then
echo "C"
elif [ "$score" -lt 80 -a "$score" -ge 60 ];then
echo "B"
else
echo "A"
fi
case判斷結構
與if/elif/else一樣,可用于多種情況下的分支選擇,語法如下:
case VAR in
var1)command1;;
var2)command1;;
var3)command1;;
var4)command1;;
...
esac
原理為從上到下依次比較VAR和var1、var2、var3的值是否相等,如果匹配相等則執行后面的命令語句,在無一匹配的情況下
匹配最后默認的*,并執行后面的默認命令。需要注意的是,var1 var2 var3等這些值只能是常量或正則表達式
[root@localhost liangjiongyao]# cat os_type.sh
!/bin/bash
OS=uname -s
case "$OS" in
FreeBSD) echo "this is Cygwin";;
SunOS) echo "this is Solaris";;
AIX) echo "this is Minix";;
Linux) echo "this is Linux";;
*) echo "Failed to identify this OS";;
esac
2018-08-25
帶參數的函數
位置參數
[root@localhost liangjiongyao]# cat checkFileExist_v2.sh
!/bin/bash
function checkFileExist(){
if [ -f $1 ];then
return 0
else
return 1
fi
}
checkFileExist $1
if [ $? -eq 0 ];then
echo "$1 exist"
else
echo "$1 not exist"
fi
使用的時候直接向腳本傳遞文件全路經的方式傳遞參數
[root@localhost liangjiongyao]# bash checkFileExist_v2.sh /etc/notExist
/etc/notExist not exist
[root@localhost liangjiongyao]# bash checkFileExist_v2.sh /etc/passwd
/etc/passwd exist
移動位置參數
前面有提到,shift命令可讓位置參數左移一位
[root@localhost liangjiongyao]# cat shift_03.sh
!/bin/bash
until [ $# -eq 0 ]
do
echo "Now $1 is: $1,total parameter is:$#"
shift
done
[root@localhost liangjiongyao]# bash shift_03.sh a b c d
Now $1 is: a,total parameter is:4
Now $1 is: b,total parameter is:3
Now $1 is: c,total parameter is:2
Now $1 is: d,total parameter is:1
如果在shift加數字n,則位置參數將會每次移動n位
下面利用shift計算腳本中所有參數的和
[root@localhost liangjiongyao]# cat shift_04.sh
!/bin/bash
tatal=0
until [ $# -eq 0 ]
do
let "tatol=tatol + $1"
shift
done
echo $tatol
[root@localhost liangjiongyao]# bash shift_04.sh 1 2 3 4
10
函數庫
對于某些常用的功能,必須考慮將其獨立出來,集中存放在一些獨立的文件中,這些文件就被稱為“函數庫”。
函數庫的本質也是“函數”,所以它的定義方式和普通函數沒有任何區別,但為了和一般函數區分開來,在實踐中建議函數庫
使用下劃線開頭。
自定義函數庫
[root@localhost liangjiongyao]# cat lib01.sh
!/bin/bash
_checkFileExist(){
if [ -f $1 ];then
echo "$1 exist"
else
echo "$1 not exist"
fi
}
其他腳本在希望直接調用_checkFileExist函數時,可以通過直接加載lib01.sh函數庫的方式實現。加載方式有如下兩種:
使用“點”命令
[root@localhost liangjiongyao]# ./PATH/TO/LIB
使用source命令
[root@localhost liangjiongyao]# source /PATH/TO/LIB
假設現在有個腳本想要直接調用_checkFileExist函數,可以通過加載lib01.sh函數庫來實現。
[root@localhost liangjiongyao]# cat lib01.sh
!/bin/bash
_checkFileExist(){
if [ -f $1 ];then
echo "$1 exist"
else
echo "$1 not exist"
fi
}
[root@localhost liangjiongyao]# cat callLib01.sh
!/bin/bash
source ./lib01.sh #當前路徑下
_checkFileExist /etc/notExistFile
_checkFileExist /etc/passwd
[root@localhost liangjiongyao]# bash callLib01.sh
/etc/notExistFile not exist
/etc/passwd exist
function函數庫
位置:/etc/init.d/functions
function函數庫中的常用函數
checkpid() 檢查某個PID是否存在
daemon() 以daemon方式啟動某個服務
killproc() 停止某個進程
pidfileofproc() 檢查某個進程的PID文件
pidofproc() 檢查某個進程的PID
status() 判斷某個服務的狀態
echo_success() 打印OK
echo_failure() 打印FAILED
echo_passed() 打印PASSED
echo_warning() 打印Waring
success() 打印OK并記錄日志
failure() 打印FAILED并記錄日志
passed() 打印PASSED并記錄日志
warning() 打印Waring并記錄日志
action() 執行給定的命令,并根據執行結果打印信息
strstr() 檢查$1字符串中是否含有$2字符串
comfirm() 提示是否啟動某個服務
遞歸函數
在函數體中繼續調用函數自身,但是遞歸函數不可能無限遞歸下去,所以它一定要有結束遞歸的條件,當滿足該條件時,遞歸就會終止。
結構如下:
function recursion(){
recursion
conditionThatEndTheRecursion #停止遞歸的條件
}
使用-x可以觀察腳本運行過程
重定向
所謂“重定向”,就是將原本應該從標準輸入設備(鍵盤)輸入的數據,改由其他文件或設備輸入;或將原本應該輸出到標準輸出設備(顯示器)的內容,
改而輸出到其他文件或設備上。
文件標識符和標準輸入輸出
文件標識符是重定向中一個很重要的一個概念,linux使用0到9的整數指明了與特定進程相關的數據流,系統在啟動一個進程的同時會為該進程打開上個文件:
標準輸入(stdin)、標準輸出(stdout)、標準錯誤輸出(stderr),分別用文件標識符0、1、2來標識。如果要為進程打開其他的輸入輸出,則需要從整數3開始標識。
默認情況下,標準輸入為鍵盤,標準輸出和錯誤輸出為顯示器。
I/O重定向
I/O重定向可以將任何文件、命令、腳本、程序或腳本的輸出重定向到另外一個文件、命令、程序或腳本
標準輸出覆蓋重定向,默認將文件標識符為1的內容重定向到指定文件中
標準輸出追加重定向
& 標準輸出重定向
< 標準輸入重定向
| 管道
如果命令由于各種原因出錯時所產生的錯誤輸出,其文件標識符為2,可以將其重定向到文件中就不會出現在顯示器上了
[root@localhost liangjiongyao]# ls -l /usr/noexc 2> 1.txt
[root@localhost liangjiongyao]# cat 1.txt
ls: cannot access /usr/noexc: No such file or directory
標識輸出重定向:>&
將標準輸出和標準錯誤同時定向到同一個文件中
[root@localhost liangjiongyao]# command > stdout_stderr.txt1 2>&1
將錯誤輸出重定向到“宇宙黑洞”
[root@localhost liangjiongyao]# command > stdout_stderr.txt2 2>/dev/null
標準輸入重定向:<
從文件讀取內容輸入,也就是將文件內容寫入標準輸入中
[root@localhost ~]# cat < helloworld.txt
hello
world
[root@localhost ~]# cat helloworld.txt
hello
world
排序
[root@localhost ~]# cat fruit01.txt
banana
carrot
apple
[root@localhost ~]# sort < fruit01.txt
apple
banana
carrot
2018-08-26 晚
for循環在按行讀取文件內容時,任何空白字符都可以作為其讀取的分隔符;而while的按行讀取就沒有這個問題,因為while是使用換行符
作為行標記的
用cut截取第一列, -f1是第一列的意思,-d是指定分隔符
[root@localhost ~]# cat user_info.txt | cut -f1 -d' '
user001
user002
user003
非交互式給用戶創建密碼
echo "password" | passwd --stdin xpp
ftp服務端配置文件 /etc/vsftpd/vsftpd.conf中
anon_upload_enable=YES #允許匿名用戶上傳文件
anon_other_write_enable=YES #允許匿名用戶覆蓋同名文件
利用md5sum校驗文件是否被修改
[root@localhost ~]# md5sum /etc/passwd > passwd.md5
[root@localhost ~]# md5sum -c passwd.md5
/etc/passwd: OK #如果沒有被修改,則打印OK
[root@localhost ~]# useradd xpp
[root@localhost ~]# md5sum -c passwd.md5
/etc/passwd: FAILED
md5sum: WARNING: 1 computed checksum did NOT match
[root@localhost ~]#
利用ssh遠程執行命令
[root@localhost ~]# ssh root@192.168.3.2 "echo $HOSTNAME"
利用腳本自動執行ssh-copy-id
[root@localhost ~]# cat expect_ssh_copy_id.sh
!/bin/bash
PASS=$1
IP=$2
auto_ssh_copy_id() {
expect -c "set timeout -1;
spawn /usr/bin/ssh-copy-id -i /root/.ssh/id_rsa.pub root@$2;
expect {
(yes/no) {send -- yes\r;exp_continue;}
Password: {send -- $1\r;exp_continue;}
eof {exit 0;}
}";
}
2018-08-27晚
iptables防火墻
4表:filter表(用于過濾)、nat表(地址或端口映射)、mangle表(對特定數據包的修改)、raw表
5鏈:prerouting 數據包進入路由決策之前
input 路由決策為本機的數據包
forward 路由決策不是本機的數據包
output 由本機產生的向外發送的數據包
postrouting 發送給網卡之前的數據包
[root@localhost ~]# ssh root@ceph-deploy
The authenticity of host 'ceph-deploy (192.168.101.2)' can't be established.
ECDSA key fingerprint is SHA256:UKNbnhKXfY/fODVDTQMcFyubYQdC0CqXuPf7hTzreHo.
ECDSA key fingerprint is MD5:e0:a9:1b:7c:2a:f0:0c:fc:01:7c:6b:7f:27:7a:5d:58.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ceph-deploy' (ECDSA) to the list of known hosts.
Last login: Wed Aug 15 12:23:15 2018 from 192.168.122.1
[root@ceph-deploy ~]# ping www.baidu.com
PING www.a.shifen.com (183.232.231.173) 56(84) bytes of data.
64 bytes from 183.232.231.173 (183.232.231.173): icmp_seq=1 ttl=52 time=6.81 ms
清空所有的規則
[root@ceph-deploy ~]# iptables -F
刪除所有自定義的鏈
[root@ceph-deploy ~]# iptables -X
丟棄進來的數據包
[root@ceph-deploy ~]# iptables -P INPUT DROP
[root@localhost ~]# ssh root@ceph-deploy
^C ###連不上了
丟棄出去的數據包
[root@ceph-deploy ~]# iptables -P OUTPUT DROP
[root@ceph-deploy ~]# ping www.baidu.com
...... ###ping不通了
[root@ceph-deploy ~]# ping 127.0.0.1
ping:sendmsg: Operation not permitted
......
目前的狀況跟拔掉網線的操作沒什么不同,而且比沒有防火墻更糟糕的是本地數據包都無法通信了。因此需要一些策略來保證一些基本功能可用,所以要設置
以下規則。
允許icmp包進入
iptables -A INPUT -p icmp --icmp-type any -j ACCEPT
允許icmp出去
iptables -A OUTPUT -p icmp --icmp-type any -j ACCEPT
[root@ceph-deploy ~]# ping www.baidu.com
...... ###仍不通
[root@ceph-deploy ~]# ping 127.0.0.1
64 bytes from 127.0.0.1......
[root@ceph-deploy ~]# ping 192.168.122.1 #網關
64 bytes from 192.168.122.1......
[root@localhost ~]# ping 192.168.122.2 #網關能ping該主機
PING 192.168.122.2 (192.168.122.2) 56(84) bytes of data.
64 bytes from 192.168.122.2: icmp_seq=1 ttl=64 time=0.267 ms
[root@localhost ~]# ssh root@192.168.122.2
^C #依舊ssh不上
允許本地數據包出
iptables -A OUTPUT -s localhost -d localhost -j ACCEPT
允許本地數據包進
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
允許以建立和相關的數據包進入
iptables -A OUTPUT -M state --state ESTABLISHED,RELATED -j ACCEPT
如果是一臺web服務器,典型的需要是能訪問80端口,但是就目前的策略而言是無法訪問的,所以需要允許80端口訪問
iptables -A INPUT -p tcp --dport 80 -j ACCEPT #外來數據包可以通過本地的80端口進來
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #保證發給對端的數據包包能通過本地80端口出去
這里使用狀態跟蹤模塊,意思是對能建立完整的連接以及為了維持該連接需要打開的其他連接所產生的數據包都是可以通過防火墻的OUTPUT鏈。
如果需要允許當前服務器訪問其他web服務器,該怎么辦?只要打開讓數據出去的80端口就可以了。
iptables -A OUTPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
如果需要訪問www.baidu.com,就必須要允許域名解釋的數據包出去,因為服務器訪問該域名之前需要先解釋出它的IP地址
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
由于管理需要ssh到這臺服務器,則需要打開22端口
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
如果只能允許一個固定的IP能ssh到該服務器的話,上面的語句需要該為
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.10 -j ACCEPT
可能還需要從該服務器ssh到別的服務器
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
自定義開機啟動項的init腳本
init腳本是linux系統用于啟動系統服務的腳本,存放路徑為/etc/init.d/。系統在啟動時將根據當前的運行等級確定
運行在/etc/rc.d/rcX.d目錄下的腳本(都是到/etc/init.d目錄中的文件軟連接)
init腳本必須接收至少兩個參數:start和stop,用于啟動和停止服務。建議在寫init腳本時,將各種參數的執行體封裝成函數的格式。
使用腳本操作MySQL數據庫
查看本地所有數據庫
[root@localhost ~]# mysql -uroot -p123 -e "show databases"
使用腳本
[root@localhost ~]# cat mysql01.sh
!/bin/bash
username="root"
password="123"
cmd="show databases"
mysql -u$username -p$password -e "$cmd"
[root@localhost ~]# bash mysql01.sh
+--------------------+
| Database |
+--------------------+
| information_schema |
| Django_ORM |
| LJY |
| ORM_mutil |
| char_set_test |
| django_demo01 |
| liangjiongyao |
| ljy |
| mysql |
| performance_schema |
| test |
+--------------------+
創建數據庫
create_db_sql="create database ${database}"
mysql -u$username -p$password -e "$create_db_sql"
創建表
create_table_sql="create table ${table}(name varchar(20),id int(10))"
mysql -u$username -p$password $dbname -e "$create_table_sql"
插入數據
insert_sql="insert into ${table} values('john',1)"
mysql -u$username -p$password $dbname -e "$insert_sql"
查詢
select_sql="select * from $table"
mysql -u$username -p$password $dbname -e "$select_sql"
更新數據
update_sql="upadte $table set id=3"
mysql -u$username -p$password $dbname -e "$update_sql"
刪除數據
delete_sql="delete from ${table}"
mysql -u$username -p$password $dbname -e "$delete_sql"
使用here Document執行SQL代碼塊
[root@localhost ~]# cat mysql02.sh
!/bin/bash
mysql -uroot -p123 << EOF
create database db01;
use db01;
create table user01(
userid int(20) not null,
username varchar(20) not null,
userpass varchar(20) not null,
age int(10) not null,
primary key(userid)
);
EOF
[[root@localhost ~]# bash mysql02.sh root@localhost ~]# bash mysql02.sh
[root@localhost ~]# bash mysql02.sh
[root@localhost ~]# mysql -uroot -p123 -e "show databases;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| Django_ORM |
| LJY |
| ORM_mutil |
| char_set_test |
| db01 |
| django_demo01 |
| liangjiongyao |
| ljy |
| mysql |
| performance_schema |
| test |
+--------------------+
[root@localhost ~]# mysql -uroot -p123 db01 -e "show tables;"
+----------------+
| Tables_in_db01 |
+----------------+
| user01 |
+----------------+
[root@localhost ~]# mysql -uroot -p123 db01 -e "select * from user01;"
[root@localhost ~]# mysql -uroot -p123 db01 -e "desc user01;"
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| userid | int(20) | NO | PRI | NULL | |
| username | varchar(20) | NO | | NULL | |
| userpass | varchar(20) | NO | | NULL | |
| age | int(10) | NO | | NULL | |
+----------+-------------+------+-----+---------+-------+
[root@localhost ~]# cat insert.sql | mysql -uroot -p123 db01
[root@localhost ~]# mysql -uroot -p123 db01 -e "select * from user01;"
+--------+----------+----------+-----+
| userid | username | userpass | age |
+--------+----------+----------+-----+
| 1 | ljy | 123 | 23 |
+--------+----------+----------+-----+
[root@localhost ~]# cat insert.sql
insert into user01 values(1,'ljy',123,23)
使用lvm備份mysql數據
使用mysqldump或mysqlhotcopy進行備份,這種方式往往會造成數據備份不一致的問題。在備份的過程中,當備份工具完成了
備份表A的備份之后,在備份表B時,可能A表記錄已經發生了變化,這樣的備份文件實際上是無法用于數據庫恢復。解決這個問題的方法是在整個備份過程中鎖定表,
直至備份結束,但這會影響網站的可用性,因為在數據庫備份過程中又與數據庫長時間被鎖定而無法寫入任何數據
lvm的快照功能可以很好地解決這個問題,在對一個lv創建snapshot時,僅會復制其中的元數據,而不回有任何真實數據的復制,所以創建過程幾乎是實時的。
當原有的LV有寫操作時,數據會寫到快照中而不是原LV中(cow),從而保證了原LV中數據的一致性。為了確保數據一致性,在對其做快照之前也需要對數據庫進行鎖定操作,
做完快照后再解除鎖。又與快照的過程極為迅速,所以短時間的數據庫鎖定并不會對前臺應用造成影響。
注意:快照創建的大小必須考慮備份期間數據的變化量,如果數據變化量大于快照的大小則會造成快照損壞,所以在對特別重要的數據進行快照備份時,要讓快照的大小必須至少等于原LV的大小
使用lvm的快照的前提是,mysql數據庫的數據文件必須存儲在LV上,數據目錄使用邏輯卷。
創建PV
pvcreate mysql_pv /dev/sdb
創建VG
vgcreate mysql_vg /dev/sdb
創建LV
lvcreate -L 1024M -n mysql_lv mysql_vg
格式話掛載
mkfs.ext4 /dev/mysql_vg/mysql_lv
掛載lv
mount /dev/mysql_vg/mysql_lv /mount
關閉mysql服務,將/var/lib/mysql/中的數據全部復制到新創建的LV中
systemctl stop mysqld
cp -a /var/lib/mysql/* /mnt
將/dev/mysql_vg/mysql_lv重新mount,并啟動mysql
umount /dev/mysql_vg/mysql_lv
mount /dev/mysql_vg/mysql_lv /var/lib/mysql/
systemctl start mysqld
最后不要忘記寫/etc/fatab文件,開機自動掛載該LV到/var/lib/mysql/
在對mysql_lv做快照之前,先使用flush tables和flush tables eith read lock強行將所有OS的緩沖數據寫入磁盤(類似于操作系統的sync),同時將數據庫設置為全局只讀。
快照完成之后,再使用unlock tables解鎖。然后只需要將快照進行掛載,復制其中的數據,在復制完成后刪除該快照即可。
轉載于:https://www.cnblogs.com/liangjiongyao/p/10318092.html
總結
以上是生活随笔為你收集整理的shell在linux里摇摇晃晃的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常见的数据类型
- 下一篇: Linux基础第四课——文件操作