Shell 快速上手

引言

Shell 是 linux 系統下很是實用的工具。經過使用 Shell,能夠提高在 linux 系統下的工做效率。javascript

Shell 學習

代碼都在這裏:https://github.com/xiang2017/shell_studyphp

變量

#!/bin/bash
# 變量
echo "01_變量.sh"

# 變量定義與賦值,等號兩邊不能用空格分開
name=hahahaha
echo $name
echo 也可使用 {} 輸出: ${name}

# 一些特殊變量
test_func() {
  echo "function name is $FUNCNAME"
}
test_func

echo $HOSTNAME
echo $HOSTTYPE
echo $MATCHTYPE
echo $LANG
echo $PWD
# echo $PATH
unset name
echo $name

# 只讀變量
readonly R0=100
R0=200
echo $?   # 上一條指令是錯誤的,因此 $? 爲非0

# 變量的做用域
# 變量的做用域又叫「命名空間」,相同名的變量能夠在不一樣命名空間定義。
# 在 Linux 系統中,不一樣進程 ID 的 Shell 默認爲不一樣的命名空間
VAR_01=100
function update() {
  # 在函數內外訪問到的是同一個變量
  VAR_01=200
}
update
echo 變量 VAR_01: $VAR_01

function update02() {
  # 可使用 local 關鍵字聲明函數內部的局部變量
  local VAR_01=300
}
update02
echo "local 聲明的本地變量不會影響全局變量,VAR_01: ${VAR_01}"

# 子 Shell 不會繼承變量
echo "echo 子 shell 的 VAR_01 爲 \$VAR_01" > tmp.sh
bash ./tmp.sh

# 導出變量(環境變量),子 Shell 可繼承,至關於子 Shell 啓動時複製了導出的變量
export VAR_01
bash ./tmp.sh

# 在子 Shell 中修改 VAR_01 不會影響

rm ./tmp.sh     # 刪除 tmp.sh

轉義和引用

#!/bin/bash
# 轉義
# 跟其餘編程語言裏的轉義同樣,使用轉義符 \
echo \# 使用轉義輸出註釋符號 \#
Dollar=123
echo \$Dollar is $Dollar
echo 8 \* 8 = 64

# 引用
# Shell 中一共有 4 中引用符,分別是 雙引號,單引號,反引號,轉義符

# "" 雙引號:部分引用,能夠解釋變量
echo "\$Dollar is $Dollar"
# 帶不帶雙引號看起來同樣,可是對於輸出空格有區別
VAR="A     B      C"
echo 不帶引號對於連續空格只輸出一個:$VAR
echo "帶引號會把全部空格輸出:$VAR"

# '' 單引號:全引用,只按照字面意思輸出內容,轉義符也不能用了
echo '$Dollar 在單引號內仍是 $Dollar。'
echo '轉義符在單引號內輸出 \,單引號只把內容做爲字面量輸出'
echo '轉義符不能用,單引號內不能輸出單引號'

# `` 反引號:命令替換,將命令的標準輸出做爲值賦給某個變量
# 命令替換也可使用 $(命令) 的形式
LS=`ls`
echo "=== LS ==="
echo $LS
echo "=== LS ==="
LSA=$(ls -a)
echo $LSA
# $() 支持嵌套
$(echo $(ls) > tmp.sh)
TMP=$(cat tmp.sh)
echo === tmp ===
echo $TMP
echo === tmp ===
rm tmp.sh

運算符

#!/bin/bash
# 運算符
# Shell 的運算符主要有:
# 比較運算符(整數比較),字符串運算符(字符串測試),文件操做運算符(用於文件測試),邏輯運算符,算術運算符,位運算符,自增自減等

# 算術運算符:加減乘除餘冪 以及加等,減等,乘等,初等,餘等
A=1
B=2
let "C = $A + $B"  # 須要使用 let 關鍵字執行運算
echo $C

# 位運算符:左移 右移 按位與 按位或 按位非 按位異或
var1=1
var2=5

let "var = $var1<<2"
echo $var
let "var = $var1&$var2"
echo $var
# 按位非就是 -($var+1)
let "var = ~8"
echo $var

# 自增自減,與其餘語言相似,分爲前置和後置的區別
var1=1
echo "var1 is $var1"
let "var2=++var1"
echo "var2 前置自增 var1,$var2"
var1=1
let "var2=var1++"
echo "var2 後置自增 var1,$var2"

# 其餘算術運算
# 使用 $[] 作運算:$[] 和 $(()) 相似,可用於簡單的算術運算
echo '$[1+1]' is $[1+1]
echo '$[5 ** 2]' is $[5 ** 2]

# 使用 expr 作運算:使用 expr 要求操做數和操做符之間用空格分開,不然會被當成字符串
expr 1+1
expr 1 + 1
expr 2 \* 2   # 特殊字符運算符須要轉義

# 算術擴展: $((算術表達式))
echo $((2*(1+1)))

# 使用 bc 作運算
# 前面介紹的運算只能基於整數,若是想要計算高精度小數,可使用 Linux 下的 bc 工具。
# bc 是一款高精度計算語言,支持順序執行,判斷,循環,函數等,下面是一個簡單的例子
NUM1=1.2
NUM2=2.3
SUM=$(echo "$NUM1+$NUM2" | bc)
echo $SUM
# 你也能夠直接在命令行下輸入 bc,而後回車進入 bc 命令行模式

特殊字符

#!/bin/bash
# 特殊字符

# 通配符
# 通配符用於模式匹配,常見的通配符有 *、? 和用 [] 括起來的字符序列。
# 例如:a* 能夠匹配以 a 開頭的任意長度的字符串,可是不能包含 點號和斜線號
# 因此 a* 不能匹配 abc.txt
# ? 能夠匹配任意單個字符
# [] 表示能夠匹配其中的任意一個,好比 [abc] 能夠匹配a或者b或者c
# [] 中能夠用 - 表示起止。好比 [a-z] 匹配全部小寫字母
# *? 在 [] 表示普通字符,沒有通配功效

# 引號
# 02_轉義和引用.sh 中介紹過,主要有單引號,雙引號,反引號

# 註釋符號

# 大括號
# 大括號 {} 在 Shell 中的用法不少
# 1. 變量擴展 ${PWD}
# 2. 通配符擴展
# 3. 語句塊
# 通配符擴展的例子:
touch file_{A,B}
ls . | grep file
rm file_A
rm file_B

# 其餘
# 位置參數
# $0: 腳本名自己
# $1,$2... 腳本的第一個參數,第二個參數...
# $# 變量總數
# $* $@ 顯示全部參數
# $? 前一個命令的退出的返回值
echo $?    # 正常退出,結果爲 0
rm qweqweqweqwe
echo $?    # 出現錯誤時,結果爲 非0
# $! 最後一個後臺進程的 ID 號

測試

#!/bin/bash
# 測試:程序運行過程當中常常須要根據實際狀況執行特定的命令,
# 好比,判斷某個文件是否存在,若是不存在,可能須要先建立該文件
# ls tmp.sh
# echo $?

# 測試結構
# 1. test expression   使用 test 指令
# 2. [expression]      使用 []

# 文件測試
# 1. test file_operator FILE
# 2. [file_operator FILE]
test -e tmp.sh
echo $?         # 不存在,上一個指令結果爲 1
[ -e tmp.sh ]
echo $?

# 文件測試符,文件不存在時,均返回假
# -b FILE 當文件存在且是塊文件時,返回真,不然爲假
# -c FILE 當文件存在且是設備文件時,返回真,不然爲假
# -d FILE 測試文件是否爲目錄
# -e FILE 測試文件或目錄是否存在
# -f FILE 測試文件是否爲普通文件
# -x FILE 判斷文件是否爲可執行文件
# -w FILE 判斷文件可寫
# -r FILE 判斷文件可讀
# -l FILE 判斷是否爲連接文件
# -p FILE 判斷是否爲管道文件
# -s FILE 判斷文件存在且大小不爲 0
# -S FILE 判斷是否爲 socket 文件
# -g FILE 判斷文件是否設置了 SGID
# -u FILE 判斷文件是否設置了 SUID
# -k FILE 判斷文件是否設置了 sticky 屬性
# -G FILE 判斷文件屬於有效的用戶組
# -O FILE 判斷文件屬於有效的用戶
# FILE1 -nt FILE2 FILE1 比 FILE2 新時返回真
# FILE1 -ot FILE2 FILE1 比 FILE2 舊時返回真

# 字符串測試
# 主要包括 等於、不等於、大於、小於、是否爲空
# -z string 爲空時返回真
echo "字符串測試"
[ -z "" ]
echo '[ -z "" ]' $?   # 結果 0,表示爲真

# -n string 非空時返回真
[ -n "aaa" ]
echo '[ -n "aaa" ]' $?
[ "string1" = "string2" ]
echo '[ "string1" = "string2" ]' $?
[ "string1" != "string2" ]
echo '[ "string1" != "string2" ]' $?
[ "string1" > "string2" ]
echo '[ "string1" > "string2" ]' $?
[ "string1" < "string2" ]
echo '[ "string1" < "string2" ]' $?

# 整數比較
# -eq 意 相等
# -gt 意 >
# -lt 意 <
# -ge 意 >=
# -le 意 <=
# -ne 意 !=
[ 1 -eq 2 ]
echo '[ 1 -eq 2 ]' $?
[ 1 -gt 2 ]
echo '[ 1 -gt 2 ]' $?
[ 1 -lt 2 ]
echo '[ 1 -lt 2 ]' $?
[ 1 -ge 2 ]
echo '[ 1 -ge 2 ]' $?
[ 1 -le 2 ]
echo '[ 1 -le 2 ]' $?
[ 1 -ne 2 ]
echo '[ 1 -ne 2 ]' $?

# 邏輯測試符與邏輯運算符
# ! expression 取反
# expression -a expression 同爲真,結果爲真        
# expression -o expression 只有有一個爲真,結果爲真
touch tmp.sh
[ ! -e tmp.sh ]
echo '[ ! -e tmp.sh ]' $?

[ -e tmp.sh -a -e tmp1.sh ]
echo '[ -e tmp.sh -a -e tmp1.sh ]' $?

[ -e tmp.sh -o -e tmp1.sh ]
echo '[ -e tmp.sh -o -e tmp1.sh ]' $?

# -a -o 能夠用 && 和 || 替代,不過寫法上會有區別
[ -e tmp.sh ] && [ -e tmp1.sh ]
echo '[ -e tmp.sh ] && [ -e tmp1.sh ]' $?

[ -e tmp.sh ] || [ -e tmp1.sh ]
echo '[ -e tmp.sh ] || [ -e tmp1.sh ]' $?

rm tmp.sh
rm string2

判斷

#!/bin/bash
# bash 的判斷與循環與其餘語言相似,有 if else elif case

# if 判斷結構
# if expression; then
#    command
# elif expression; then
#    command
# else
#    command
# fi
if [ ! -e tmp.sh ];
then
  echo "tmp.sh 不存在,建立它"
  touch tmp.sh

  if [ -e tmp.sh ]; then
    echo "tmp.sh 建立好了"
  else
    echo "tmp.sh 建立失敗"
  fi
else
  echo "tmp.sh 存在,刪了它"
  rm tmp.sh
fi

# case 判斷結構
# case VAR in
# var1) command ;;
# var2) command ;;
# ...
# *) command ;;
# esac
read -p "請輸入數字:" NUM
case $NUM in
1) echo "輸入爲 1" ;;
2) echo "輸入爲 2" ;;
*) echo "輸入爲 其餘" ;;
esac

rm tmp.sh

循環

#!/bin/bash
# 循環
# Shell 的循環主要有 for、while、until、select 幾種

# for 循環
# 帶列表的 for 循環:
# for VAR in (list)
# do
#   command
# done
for NUMBER in 1 2 3 4 5
do
  echo $NUMBER
done

fruits="apple banana orange"
for FRUIT in ${fruits}
do
  echo $FRUIT
done

# 循環數字時可使用 {a..b} 表示從 a 循環到 b
for N in {2..10}
do
  echo $N
done
# 其中 {2..10} 能夠用 seq 命令替換
echo "echo with seq:"
for N in $(seq 2 10)
do
  echo $N
done
# seq 命令能夠加 「步長」
for N in $(seq 1 2 20)
do
  echo $N
done

# 能夠看出,for in 後面的內容能夠是任意命令的標準輸出
# 好比,咱們能夠輸出當前目錄下的全部帶 sh 的文件
for VAR in $(ls | grep sh)
do
  echo $VAR
done

# 若是 for 後面沒有 in ,則至關因而 in $@
# 你能夠執行 bash 07_循環.sh a b c 試一試
for VAR
do
  echo $VAR
done

# 類 C 的 for 循環
# for ((exp1; exp2; exp3))
# do
#   command
# done
for ((i=0, j=100; i < 10; i ++))
do
  echo $i $j
done


# while 循環
# 語法以下:
# while expression
# do
#   command
# done
# while ((1)) 會無限循環
COUNT=0
while [ $COUNT -lt 5 ]
do
  echo $COUNT
  let "COUNT++"
done

# while 按行讀取文件
echo "john  30  boy
sue   20  girl" > tmp.txt
while read LINE
do
  NAME=`echo $LINE | awk '{print $1}'`
  AGE=`echo $LINE | awk '{print $2}'`
  SEX=`echo $LINE | awk '{print $3}'`
  echo $NAME $AGE $SEX
done < tmp.txt    # 輸入重定向
rm tmp.txt


# until 循環
# until 與 while 相似,區別在於 until 判斷爲 否,會繼續循環,而 while 判斷爲 真,才繼續循環
# until ((0)) 會無限循環
COUNT=0
until [ $COUNT -gt 5 ]
do
  echo $COUNT
  let "COUNT++"
done


# select 循環
# select 是一種菜單式的循環方式,語法結構與 for 類似,每次循環的值由用戶選擇
echo "choose your menu:"
select MENU in "apple" "banana" "orange" "exit"
do
  echo "you choose $MENU"
  if [[ $MENU = "exit" ]]
  then
    break
  else
    echo "choose again"
  fi
done

# 循環控制,break continue,與其餘編程語言一致

函數

#!/bin/bash
# 函數

# 函數的定義
# function FUNCTION_NAME() {
#   command
# }
# 省略 function 關鍵字
# FUNCTION_NAME() {
#   command
# }            
function func1 {
  echo 1 
}
func2() {
  echo 2 
}

# 函數調用
func1
func2

# 函數返回值
func3 () {
  echo '請輸入函數的返回值:'
  read N
  return $N
}
func3
echo "上個函數的返回值是" $?   # 使用 $? 獲取上一條指令的返回值

# 函數參數
# 與腳本的參數使用一致
func4 () {
  echo "第一個參數 $1"
  echo "第二個參數 $2"
  echo "全部參數 $@"
  echo "參數數量 $#"
}
func4 a b c

# 使用 set 能夠指定位置的腳本(或函數)參數值
func5() {
  set q w e
  echo "參數1 $1"
  echo "全部參數: $@"
}
func5

# 移動位置參數:在 Shell 中可使用 shift 命令把參數左移一位
func6() {
  while [ $# -gt 0 ]
  do
    echo current \$1 is $1
    shift
  done
}
func6 q w e r t

# 實現一個 pow 函數
pow() {
  let "r=$1**$2"
  return $r

}
pow 2 5
echo $?

重定向

#!/bin/bash
# 重定向
# 重定向是指將本來由標準輸入輸出的內容,改成輸入輸出的其餘文件或設備

# 系統在啓動一個進程時,會爲該進程打開三個文件:
# 標準輸入(stdin)、標準輸出(stdout)、標準錯誤(stderr)
# 分別用文件標識符 0、一、2 標識
# 若是要爲進程打開其餘的輸入輸出,須要從證書 3 開始標識
# 默認狀況下,標準輸入爲鍵盤,標準輸出和標準錯誤爲顯示器


# 常見的 IO 重定向符號
# > 標準輸出覆蓋重定向,將命令的標準輸出重定向到其餘文件中,會直接覆蓋原文件內容
# >> 標準輸出追加劇定向,將命令的標準輸出重定向到其餘文件中,不會覆蓋文件,會在文件後面追加
# >& 標識輸出重定向,講一個標識的輸出重定向到另外一個標識的輸入
# < 標準輸入重定向,命名將從指定文件中讀取輸入,而不是從鍵盤中讀取輸入
# | 管道,從一個命令中讀取輸出,做爲另外一個命令的輸入


# 輸出重定向
# 把本來標準輸出到屏幕的內容,重定向到 tmp.txt 文件中
echo "result1" > tmp.txt
cat tmp.txt

echo "result2" > tmp.txt
cat tmp.txt

# 輸出追加
echo "輸出追加:"
echo "result3" >> tmp.txt
echo "result3" >> tmp.txt
echo "result3" >> tmp.txt
cat tmp.txt
rm tmp.txt

# 標識輸出重定向
echo "未重定向標準錯誤,會直接輸出到頁面"
# 制定一個不存在的命令
adhfafahdfakdf > tmp.txt
echo "tmp.txt:" `cat tmp.txt`
rm tmp.txt
echo "重定向標準錯誤到標準輸出,會輸出到文件中"
asiiaodfuoaf > tmp.txt 2>&1
echo "tmp.txt:" `cat tmp.txt`

# 標準輸入重定向
echo "標準輸入重定向:"
while read Line
do
  echo $Line
done < tmp.txt

# 管道
# 獲取 .sh 文件的名稱
ls | grep .sh | cut -f1 -d'.'

# 使用 exec
# exec 是 Shell 的內建命令,執行這個命令時,系統不會啓動新的 Shell,而是用被執行的命令替換當前的 Shell 進程
# 所以,在執行完 exec 的命令後,該 Shell 進程將會主動退出
# 例如:執行 exec ls ,後續的其餘命令將不會執行。你也能夠直接打開 Shell,執行 exec ls 試試
# 此外,exec 還能夠用於 I/O 重定向。
# exec < file 將 file 文件中的內容做爲 exec 的標準輸入
# exec > file 將 file 文件做爲標準輸出
# exec 3<file 指定文件標識符
# exec 3<&- 關閉文件標識符
# exec 3>file 將寫入文件標識符的內容寫入到指定文件(輸出重定向)
# exec 4<&3 建立文件標識符4,4是3的拷貝 (相似標識輸出重定向 2>&1)
# 注:不一樣的 shell 環境可能會有所差異,好比我在 mac 的 zsh 下就不能正常使用 exec 重定向

# Here Document
# here doc 又稱爲 此處文檔,用於在命令或腳本中按行輸入文本。
# 格式爲 command << delimiter
# delimiter 是用於標註結束的分隔符

# 示例:
# 你能夠在命令行下輸入 sort << END 試試
# 你能夠在命令行下輸入 cat > tmp.txt << END 試試
cat << EOF > tmp.txt
1
2
3
EOF
cat tmp.txt

rm tmp.txt

數組

#!/bin/bash
# 數組

# bash 只支持一維數組

# 定義數組
declare -a mArray
mArray[0]="nihao"
mArray[1]=2

# 定義時賦值,數組的元素用空格分開,其餘字符會被當成值,好比 "php", 會被當成 php,
declare -a mArray=("php" "python" 123)

# 數組取值,須要用 ${數組名[索引]} 語法
echo ${mArray[0]}
echo ${mArray[1]}
echo ${mArray[2]}
# 使用 @ * 能夠索引所有元素
# @ 獲得以空格分開的元素值
# * 獲得整個字符串
echo ${mArray[@]}
echo ${mArray[*]}

# 數組長度
echo "數組長度是 ${#mArray[@]}"
echo "數組長度是 ${#mArray[*]}"

# 數組截取
# 能夠獲取子數組,下面示例爲獲取數組的第 一、2 下標位置的元素
echo ${mArray[@]: 1:2}
# 能夠獲取數組中某個元素的若干字符,下面示例爲獲取數組中第二個元素的 從0開始 3個字符
echo ${mArray[1]: 0:3}

# 合併數組
Front=("javascript" "typescript")   # 數組聲明也能夠忽略 declear -a
Conn=(${mArray[@]} ${Front[@]})
echo ${Conn[@]}
echo ${#Conn[@]}            # 合併獲得數組的長度

# 替換元素
mArray=(${mArray[@] /123/"java"})
echo ${mArray[@]}

# 取消數組或元素
unset mArray[1]
echo "取消下標爲 1 的元素後,數組爲:${mArray[@]},數組長度爲 ${#mArray[@]}"
# 須要注意的是,數組的 1 位置的元素變爲了空,而不是後面的元素向前移動
echo "數組 1 位置的元素爲 ${mArray[1]}, 2 位置的元素爲 ${mArray[2]}"

字符處理

#!/bin/bash
# 字符處理

# 管道
# 從一個命令中讀取輸出,做爲另外一個命令的輸入
# 示例
# ls | grep .sh | cut -f1 -d'.'

# grep
# grep 是基於行的文本搜索工具,該命令經常使用的參數有:
# grep [-ivnc] '須要匹配的字符' 文件名
# -i 不區分大小寫
# -c 統計包含匹配的行數
# -n 輸出行號
# -v 反向匹配
# 其中 '須要匹配的字符' 支持正則表達式模式
grep -in 'func' 01_變量.sh

# sort
# sort 能夠對無序的數據進行排序
# sort [-ntkr] 文件名
# -n 採起數字排序
# -t 指定分隔符
# -k 指定第幾列
# -r 反向排序

# 示例 使用空格分開每行,按第二列進行排序
echo "3 1 3
1 2 4
5 3 2
1 2 4
5 3 4
2 3 4" | sort -t ' ' -k 2

# uniq
# 使用 uniq 能夠刪除重複內容
echo "123
123
ab
ab" | uniq

# cut 截取文本
# cut -f指定的列 -d'分隔符'
# 指定的列能夠用逗號分隔開,或者使用範圍
echo "jhon 10 boy class1
lili 12 girl class2" | cut -f2-4 -d ' '

# tr 作文本轉換
# tr '原字符' '目標字符' 其中原字符與目標字符一一對應
head -n 5 01_變量.sh | tr '[a-z]' '[A-Z]'

# paste 進行文本合併
# paste 會把文本按行合併。
# paste -d
echo "1
2
3" > tmp1.txt
echo "a
b
c" > tmp2.txt
paste -d: tmp1.txt tmp2.txt > tmp.txt
cat tmp.txt

# split 分割大文件
# split -l lines file dist_file
# 示例
split -l 5 01_變量.sh split_file
ls | grep split_file
rm split_file*

# sed 與 awk
# ...
# 若是現有工具不能知足你對字符串處理的需求,那就去了解一下 sed 和 awk 命令。

rm tmp*

示例-操做數據庫

#!/bin/bash
USER=root
PASSWORD=root

# 使用 -e 執行
databases=`mysql -u$USER -p$PASSWORD -e"show databases"`

for db in $databases
do
  echo "Tables in $db:"
  # 使用 here doc 執行代碼塊
  mysql -u$USER -p$PASSWORD << EOF
use $db; 
show tables;
EOF
  # 也可使用輸入重定向
  # mysql -u$USER 0pPASSWORD < select.sql
done
相關文章
相關標籤/搜索