1、man
manual 是類 UNIX 系統(tǒng)的標準手冊工具,共 9 卷,用 man man可以看到 9 卷分別是什么內(nèi)容:
1 Executable programs or shell commands 可執(zhí)行程序或shell命令
2 System calls (functions provided by the kernel) 系統(tǒng)調(diào)用
3 Library calls (functions within program libraries) 庫調(diào)用
4 Special files (usually found in /dev) 特殊文件
5 File formats and conventions eg /etc/passwd 文件格式和約定
6 Games 游戲
7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) 雜項(包括宏和慣例)
8 System administration commands (usually only for root) 系統(tǒng)管理命令(通常僅適用于root用戶)
9 Kernel routines [Non standard] 內(nèi)核例程(非標準)
如果只有一個含義,直接查詢即可,比如man ls,但有很多是一詞多義的:chmod, chown, chmod, stat, exit, info, socket, flock, open, write, printf, passwd 等等。
man 1 printf 表示 shell 命令,
man 3 printf 表示 C 語言標準庫函數(shù),
可以用 -a 選項顯示全部,比如 man -a printf ,按 q 退出之后,可以按回車鍵表示繼續(xù)看下一卷,或按 Ctrl-C 直接退出。
2、#!/bin/bash
#!/bin/bash放在 shell 腳本開頭,是指此腳本使用/bin/bash來解釋執(zhí)行。
#!/usr/bin/python 也是類似的, 表示用/usr/bin/python來解釋執(zhí)行 python 腳本,加了這行就可以用./xx.py的方式來運行了。
#!/usr/bin/env python 會去環(huán)境變量路徑中尋找 python 目錄。
3、shell 運行方式
(1) bash 1.sh
創(chuàng)建子進程運行 1.sh,需要有 r 權(quán)限。
(2) ./1.sh
創(chuàng)建子進程運行 1.sh,需要有 r 和 x 權(quán)限。
(3) source 1.sh
當前進程運行 1.sh,需要有 r 權(quán)限,
(4) . 1.sh
點命令是 source 命令的另一種寫法。
如何證明是在當前進程運行,還是創(chuàng)建子進程運行呢,下面用 2 種簡單的方法:
(1) 創(chuàng)建一個子目錄,然后用 cd 命令
~/test$ mkdir child
~/test$ echo 'cd child;pwd' > 1.sh
使用 bash 執(zhí)行結(jié)束后,可以看左邊的提示符,當前的工作目錄沒有改變,cd 命令只對子進程產(chǎn)生影響:
~/test$ bash 1.sh
/home/oo/test/child
~/test$
使用 source執(zhí)行結(jié)束后,當前目錄變成了 child 目錄,因為 cd 命令對當前進程產(chǎn)生了影響:
~/test$ source 1.sh
/home/oo/test/child
~/test/child$
(2) 變量賦值
bash是創(chuàng)建子進程來執(zhí)行腳本,所以子進程的變量,在父進不能訪問;
而source在當前進程執(zhí)行,所以腳本中對變量的賦值,當前進程能訪問:
$ echo "a=99" > 1.sh
$ bash 1.sh
$ echo $a
$ source 1.sh
$ echo $a
99
4、內(nèi)部命令,外部命令
通過 type 命令可以查詢某個命令是內(nèi)部命令、外部命令,還是別名(alias):
$ type cd
cd is a shell builtin
$ type find
find is /usr/bin/find
$ type ls
ls is aliased to `ls --color=auto'
如果用 bash shell,可以通過help命令列出所有內(nèi)部命令列表。
區(qū)別:
(1)內(nèi)部命令不創(chuàng)建子進程,對當前進程有影響,執(zhí)行速度快;
外部命令不在shell中,保存在一個獨立的文件中,會創(chuàng)建新的進程來執(zhí)行。
(2)內(nèi)部命令查詢幫助信息用:help 內(nèi)部命令,比如 help cd,
外部命令查詢幫助信息用:外部命令 --help,比如 ls --help。
當然除了 man、help 之外,info 也可以用于查詢幫助信息。
5、重定向
(1) 輸入重定向 <
$ wc -l < /etc/passwd
32
$ echo 123 > 1.txt
$ read var < 1.txt
$ echo $var
123
(2) 輸出重定向
① > 先清空該文件,再追加內(nèi)容。
② >> 文件的內(nèi)容不變,在文件末尾追加內(nèi)容。
③ 2> 錯誤重定向
$ nothing
nothing: command not found
$ nothing 2> 1.txt
$ cat 1.txt
nothing: command not found
④ &> 表示無論是正確還是錯誤,都輸出重定向。
(3) 輸入、輸出重定向的組合使用
將開始標記 和結(jié)束標記之間的內(nèi)容作為 cat的輸入,cat 的輸出重定向到文件 1.txt:
$ cat > 1.txt << EOF
> 123
> 456
> EOF
$ cat 1.txt
123
456
這里的 EOF 只是開始和結(jié)尾的 tag,也可以換成其他的,比如ABCD:
$ cat > 1.txt << ABCD
> 1234
> 5678
> ABCD
$ cat 1.txt
1234
5678
6、變量
(1) 賦值, = 的兩邊不能有空格,否則被解釋為命令:
$ a=123
$ echo $a
123
$ a =123
a: command not found
$ a= 123
123: command not found
(2) 算數(shù)運算用 let ,否則默認是字符串
$ a=1+2+3
$ echo $a
1+2+3
$ let a=1+2+3
$ echo $a
6
(3) 把命令賦值給變量,意義不大:
$ a=ls
$ $a -l
(4) 把命令的結(jié)果賦值給變量:
$ a=$(find . -name *.sh)
$ echo $a
(5) 作用范圍
變量只在當前 shell 生效,子shell、父shell 都不能共用。
使用 export 命令之后,子shell 可以獲取 父shell 的變量。其他終端不可以。
(6)變量不用了之后,可以刪除: unset 變量名。
7、環(huán)境變量
(1)env 命令可以列出所有環(huán)境變量,一般是大寫。
PATH 指定命令搜索路徑,
PS1 可以使當前終端顯示更友好,
UID 用戶ID,
USER 用戶名。
PWD 當前路徑,
LD_LIBRARY_PATH 、LD_PRELOAD 程序運行時,搜索動態(tài)庫路徑。
(2) set 命令可以列出系統(tǒng)中已經(jīng)存在的 shell 變量,比 env 命令列出的變量更多。
$ abcde=1234567
$ set | grep abcde
abcde=1234567
(3) 預定義變量
$$ :當前進程的PID號
$0 :當前程序或腳本的名稱
$? :用于返回上一條命令是否成功執(zhí)行。如果成功執(zhí)行,將返回數(shù)字0,否則返回非零數(shù)字(通常情況下都返回數(shù)字1)。
$* 或 $@ :保存?zhèn)鬟f給腳本或進程的所有參數(shù)
$# :腳本參數(shù)的個數(shù),(類似C語言的 argc)
$1 、 $2 、 ${10} 等等 :位置變量,從命令行給腳本傳參(類似C語言的argv[]),還有函數(shù)傳參時會使用。
(4) 環(huán)境變量的配置文件
/etc/profile 是所有用戶共用的。
~/.bashrc 是某一個用戶特有的。
如果存在重復定義的變量,/etc/profile 先執(zhí)行,會被后執(zhí)行的 ~/.bashrc 覆蓋。萬一有變量被重復定義了,執(zhí)行順序很重要,可以在這 2 個文件中添加echo 輸出重定向到文件,來判斷誰先被執(zhí)行。
另外:
su root 是 non-login shell,不會加載所有的配置文件,不建議使用;
su - root 是 login shell,會加載所有配置文件,推薦使用這種方式。
8、數(shù)組
定義數(shù)組用小括號,例如: a=(1 2),
訪問所有元素 ${a[@]},
數(shù)組元素的個數(shù) ${#a[@]},
訪問單個元素 ${a[0]},負號表示倒數(shù)第幾個 ${a[-1]}。
$ a=( 11 22 33 )
$ echo ${a[@]}
11 22 33
$ echo ${a[*]}
11 22 33
$ echo ${#a[@]}
3
$ echo ${a[0]}
11
$ echo ${a[1]}
22
$ echo ${a[-1]}
33
$ echo ${a[-3]}
11
9、特殊字符
# 注釋
; 分號用來分隔兩條命令。
` 反引號 與 $() 都是用來執(zhí)行命令的。
" 雙引號(不完全引用)會對變量解釋,
' 單引號(完全引用)不解釋變量。
$ a=hello
$ echo "$a"
hello
$ echo '$a'
$a
10、整數(shù)運算
默認會解釋為字符串,所以需要用 expr,注意空格:
$ expr 1 + 2 + 3
6
$ expr 1+ 2 + 3
expr: syntax error
除了 + 還有 - * / % > < 等等,注意轉(zhuǎn)義字符:
$ expr 1 - 2
-1
$ expr 6 \* 6
36
$ expr 30 / 6
5
$ expr 32 % 3
2
$ expr 3 \> 4
0
$ expr 3 \< 4
1
$ expr 3 \<= 3
1
$ expr 3 \!= 3
0
$ expr 3 \!= 4
1
不支持小數(shù)運算:
$ expr 5 + 0.5
expr: non-integer argument
另外 length 可以計算字符串長度:
$ expr length "hello"
5
**表示乘方,expr沒有乘方功能:
$ let a=2**3
$ echo $a
8
let 簡寫為雙圓括號:
$ ((a=5+6))
$ echo $a
11
11、園括號()里面的賦值,不會影響外面。
$ a=10;echo $a
10
$ (a=20;echo $a)
20
$ echo $a
10
是圓括號()創(chuàng)建了一個子進程嗎,還是其他原因呢?
可以做一個小實驗,先打開 2 個終端:
先查看有幾個 bash 進程,可以看到是 5 個:
$ ps aux |grep bash |wc -l
5
$ (a=20;echo $a;sleep 9)
把圓括號睡眠一下,趁這個時間再次查看 bash 數(shù)量,比剛才多了 1 個,等 sleep 結(jié)束之后再看 bash 數(shù)量又恢復 5個,說明圓括號()確實創(chuàng)建了子進程:
$ ps aux |grep bash |wc -l
6
另外也可以再通過 cd 命令來看,圓括號中改變了工作目錄,并不影響父進程:
$ pwd
/home/oo/test
oo@uu:~/test$ (a=20; cd ..; pwd)
/home/oo
oo@uu:~/test$ pwd
/home/oo/test
12、test 命令
test 命令是通過設置 $? 的值, 來讓我們可以判斷是否正常退出,或者條件是否為真,0 為 真,非 0 為假。
test 通常有三類測試:字符串、數(shù)字、文件,可以通過 man 查詢具體用法:
man test|grep INTEGER
man test|grep STRING
man test|grep FILE
通過 man test 可以看到 test EXPRESSION 和 [ EXPRESSION ] 兩種寫法,
有時為了程序美觀,可以把 test 換成方括號[ ] 的寫法。
(1) 字符串
字符串是否相等,會區(qū)分大小寫:
$ [ "abc" = "abc" ]
$ echo $?
0
$ [ "abc" = "a" ]
$ echo $?
1
(2) 整數(shù)
[] 擴展為 [[]] ,可以支持更多運算符。
$ test 3 -gt 4
$ echo $?
1
$ [ 3 -gt 4 ]
$ echo $?
1
$ [[ 3 > 4 ]]
$ echo $?
1
(3) 文件
文件是否存在:
$ test -f /etc/passwd
$ echo $?
0
$ test -f /etc/passwd.1234
$ echo $?
1
目錄存在:
$ test -d /etc
$ echo $?
0
文件或目錄存在:
$ test -e /etc
$ echo $?
0
$ test -e /etc/passwd
$ echo $?
0
13、大括號
(1) 表示某個范圍內(nèi)的值,主要是給 for 循環(huán)使用的
$ echo {0..3}
0 1 2 3
(2) 減少重復書寫
cp -v /etc/passwd{,.bak} 相當于 cp -v /etc/passwd /etc/passwd.bak
14、if
(1)if-then-fi
(2)if-then-else-fi
(3)if-then-elif-then-else-fi
let a=1-6
if [ $a -eq 0 ];
then
echo "a=0"
elif [ $a -lt 0 ];
then
echo "a<0"
else
echo "a>0"
fi
15、case-esac
a=3
case "$a" in
1)
echo "case 1"
;;
2|3)
echo "case 2 or 3"
;;
*)
echo "case *"
;;
esac
case 語句的例子在很多文件都有,比如 grep -n case ~/.bashrc
16、for
for i in {1..3}
do
echo $i
done
for filename in /etc/profile.d/*.sh
do
if [ -x $filename ]; then
source $filename;
echo "source $filename;";
fi
done
下面是 C 語言風格的寫法:
for ((i=1;i<=3;i++))
do
echo $i
done
17、while
a=1
while [ $a -le 3 ];
do
echo $a;
((a++));
done
用冒號表示條件為真,寫一個死循環(huán):
while :
do
echo "always"
done
18、until
until 循環(huán)與 while 語法一致,只是條件為假才執(zhí)行循環(huán):
a=8
until [ $a -lt 5 ];
do
echo $a;
((a--));
done
19、break 與 continue
for n in {1..5}
do
if [ $n -eq 3 ];then
break;
fi
echo $n
done
for n in {1..5}
do
if [ $n -eq 3 ];then
continue;
fi
echo $n
done
20、字符串截取命令
basename 命令,可以用來刪掉后綴名:
$ basename hello.c.diff .diff
hello.c
$ basename hello.c.diff .c.diff
hello
dirname用于獲取目錄
$ dirname /etc/hosts
/etc
其實它們不在乎這個目錄是否真實存在,只是簡單的處理一下字符串而已:
$ dirname /1/2/3
/1/2
21、函數(shù)
函數(shù)定義的格式:
函數(shù)名(){ }
$ testfun(){ echo hello;};testfun;
hello
避免函數(shù)內(nèi)部的變量影響到外面,使用 local 關鍵字限定作用域為該函數(shù)。