Shell學習篇:BASH 環境

一 什麼是SHELL

shell一般代表兩個層面的意思,一個是命令解釋器,比如BASH,另外一個就是shell腳本。本節我們站在命令解釋器的角度來闡述shell

命令解釋器SHELL的發展歷史,SH-CSH-KSH-TCSH-BASH,我們先來了解下命令解釋器BASH。

二 命令的優先級

命令分為:

==> alias
  ==> Compound Commands
    ==> function
      ==> build_in
        ==> hash
          ==> $PATH
            ==> error: command not found

獲取一個命令會按照上述優先級取尋找,先找同名的alias命令,再找compound命令。。。

=================================part1

讓我們先從最簡單的入手:別名、內部命令、外部命令,來探討它們三者的優先級

別名:別名命令是為了簡化輸出給一個長參數命令的整合,別名的定義方法 alias la='ls -al' 取消別名 unalias la
內部命令:是BASH自帶的命令 功能簡單,內部命令的幫助在builtin(1)裡
外部命令:是就是一個小程序存在於/bin/ /sbin/ /usr/bin 等地方


[root@seker ~]#
[root@seker ~]# alias cd
-bash: alias: cd: not found
cd是一個內部命令 屬於bash軟件自帶命令(參考man cd) 它沒有定義別名
[root@seker ~]#
[root@seker ~]# alias ls
ls被定義了別名
alias ls='ls --color=tty'
[root@seker ~]#
[root@seker ~]# which ls
alias ls='ls --color=tty'
/bin/ls
[root@seker ~]#
ls實際是一個外部命令 屬於可執行程序 是通過C代碼編譯得出的可執行程序
[root@seker ~]# file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
[root@seker ~]#


驗證:別名是優先於內部命令和外部命令的

[root@MiWiFi-R3-srv ~]# alias cd='ls -l' #建立一個別名是cd但實際指向的是/bin/ls的別名
[root@MiWiFi-R3-srv ~]# cp /bin/hostname /usr/local/sbin/cd #在PATH第一個目錄裡放入一個cd命令
You have new mail in /var/spool/mail/root


[root@MiWiFi-R3-srv ~]# cd #此時執行cd命令 是找到得別名cd優先
total 900
-rw-r--r--. 1 root root 0 Mar 20 10:10 4.txt
-rw-------. 1 root root 956 Mar 8 09:17 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Mar 20 10:09 a.txt
-rw-r--r--. 1 root root 0 Mar 20 10:10 D.txt
drwxr-xr-x. 9 jack jack 4096 Mar 19 14:40 nginx-1.10.3
-rw-r--r--. 1 root root 911509 Mar 19 12:49 nginx-1.10.3.tar.gz
[root@MiWiFi-R3-srv ~]# unalias cd #刪除了別名,此時在去搜索 就是內部命令優先 得到了真正的cd命令


[root@MiWiFi-R3-srv ~]# cd #執行的就是系統內置的cd

[root@MiWiFi-R3-srv ~]# /usr/local/sbin/cd #此時想越過內部命令去執行外部命令 就是之前cp /bin/hostname /bin/cd留下的cd
MiWiFi-R3-srv

[root@seker ~]# rm -rf /usr/local/sbin/cd
[root@seker ~]#

小結一:命令的執行搜索順序
==>別名 (alias可以查看)
  ==> bash內部命令
    ==> $PATH 中按冒號分割的每個路徑中去搜索

ps:登陸後的預置別名從何而來(別名應該學會的東西:取消別名和建立別名,固化別名配置我們會在後續章節介紹,無非將alias定義到文件中):

[root@MiWiFi-R3-srv ~]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

[root@seker ~]#

一部分來自/root/.bashrc 普通用戶的.bashrc不包含別名,因為普通用戶的.bashrc模板是:/etc/skel/.bashrc
而root這個.bashrc在安裝完系統就預置了.


[root@MiWiFi-R3-srv ~]# grep '^alias' /root/.bashrc
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
[root@seker ~]#
一部分來自/etc/profile.d/目錄裡的可執行文件

[root@MiWiFi-R3-srv ~]# grep -rn 'alias' /etc/profile.d/
/etc/profile.d/colorgrep.csh:9:alias grep 'grep --color=auto'
/etc/profile.d/colorgrep.csh:10:alias egrep 'egrep --color=auto'
/etc/profile.d/colorgrep.csh:11:alias fgrep 'fgrep --color=auto'
/etc/profile.d/colorgrep.sh:5:alias grep='grep --color=auto' 2>/dev/null
/etc/profile.d/colorgrep.sh:6:alias egrep='egrep --color=auto' 2>/dev/null
/etc/profile.d/colorgrep.sh:7:alias fgrep='fgrep --color=auto' 2>/dev/null
/etc/profile.d/which2.csh:5:# alias which 'alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
/etc/profile.d/which2.sh:4:alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
=================================part2

內置命令bash built-in commands
很多種
第一種:bash自身帶的功能 參見man cd
NAME
bash, :, ., [, alias, bg, bind, break, builtin, cd, command, compgen, complete,
continue, declare, dirs, disown, echo, enable, eval, exec, exit, export, fc, fg,
getopts, hash, help, history, jobs, kill, let, local, logout, popd, printf,
pushd, pwd, read, readonly, return, set, shift, shopt, source, suspend, test,
times, trap, type, typeset, ulimit, umask, unalias, unset, wait

第二種:Compound Commands
例如 for if while等
參見 man bash
第三種:function
[root@MiWiFi-R3-srv ~]# function cd() { echo 'my function cd'; }
有什麼函數可以通過set命令找到
[root@MiWiFi-R3-srv ~]# set |grep cd

cd ()


echo 'my function cd'

驗證:alias,compund優先級

[root@MiWiFi-R3-srv ~]# alias if='ls -l'
[root@MiWiFi-R3-srv ~]#
[root@MiWiFi-R3-srv ~]# if #證明別名比compound的優先級高
total 900
-rw-r--r--. 1 root root 0 Mar 20 10:10 4.txt
-rw-------. 1 root root 956 Mar 8 09:17 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Mar 20 10:09 a.txt
-rw-r--r--. 1 root root 0 Mar 20 10:10 D.txt
drwxr-xr-x. 9 jack jack 4096 Mar 19 14:40 nginx-1.10.3
-rw-r--r--. 1 root root 911509 Mar 19 12:49 nginx-1.10.3.tar.gz


驗證:compound與function的優先級

[root@MiWiFi-R3-srv ~]# function for() { echo 'my function for'; }
[root@MiWiFi-R3-srv ~]# for((i=1;i<3;i++));do echo $i;done #執行的仍然是compound命令,證明compound優先級比函數高
1
2

驗證:compound與function的優先級

[root@MiWiFi-R3-srv ~]# function cd() { echo 'my function cd'; }
[root@MiWiFi-R3-srv ~]# cd #執行的是自己的函數而不是內置命令cd,證明函數比內置命令優先級高
total 900
-rw-r--r--. 1 root root 0 Mar 20 10:10 4.txt
-rw-------. 1 root root 956 Mar 8 09:17 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Mar 20 10:09 a.txt
-rw-r--r--. 1 root root 0 Mar 20 10:10 D.txt
drwxr-xr-x. 9 jack jack 4096 Mar 19 14:40 nginx-1.10.3
-rw-r--r--. 1 root root 911509 Mar 19 12:49 nginx-1.10.3.tar.gz


小結二
==> alias
  ==> Compound Commands
    ==> function
      ==> build_in

=================================part3
當別名和內部命令都搜到不到時 找$PATH

[root@MiWiFi-R3-srv ~]# sed -n 's/:/\\n/gp' <<< $PATH
/usr/local/sbin
/usr/local/bin
/sbin
/bin
/usr/sbin
/usr/bin
/root/bin


因為這個變量中包含的路徑太多,每個路徑中的可執行文件也很多
如果每次都要搜索每個路徑下的所有可執行文件,顯然是不明智的
為了減少$PATH的搜索,上一次搜索的內容能夠被下一次執行重用
bash對從$PATH中搜索得出的外部命令建立一個hash表,用於緩存
這個緩存是會話級別獨立擁有的.不可以對其他終端共享,因為每個用戶的$PATH可能不同

[root@MiWiFi-R3-srv ~]# hash -r #清除緩存
[root@MiWiFi-R3-srv ~]# hostname #每執行一次就緩存一次
MiWiFi-R3-srv


[root@MiWiFi-R3-srv ~]# hash #查看緩存表,發現多了一條緩存

hits command

1 /bin/hostname

[root@MiWiFi-R3-srv ~]# mv /bin/hostname /sbin/ #將hostname命令移動到另外一個目錄
[root@MiWiFi-R3-srv ~]# hostname #再次執行hostname,仍然是從緩存中讀hostname所在的位置,證明hash優先級比path高,hash緩存的是path路徑,而內置命令如cd等沒有路徑,因而無需羅嗦測試,內置的優先級肯定是比hash要高的
bash: /bin/hostname: No such file or directory

總結命令執行的獲取順序:
==> alias
  ==> Compound Commands
    ==> function
      ==> build_in
        ==> hash
          ==> $PATH
            ==> error: command not found

三 元字符

bash中的特殊字符,鍵盤上能敲出來的特殊字符都有其特殊意義,強調一點:元字符是被shell解釋的

`` 命令替換 取命令的執行結果

[root@MiWiFi-R3-srv ~]# ls
4.txt anaconda-ks.cfg a.txt B.txt c.txt D.txt nginx-1.10.3 nginx-1.10.3.tar.gz
[root@MiWiFi-R3-srv ~]# res=`ls` #取命令的運行結果,賦值給變量res


[root@MiWiFi-R3-srv ~]# echo $res #查看變量res的值
4.txt anaconda-ks.cfg a.txt B.txt c.txt D.txt nginx-1.10.3 nginx-1.10.3.tar.gz

$()同上,但它彌補了``的嵌套缺陷

[root@MiWiFi-R3-srv ~]# res=`echo `ls`` #嵌套使用後無法達到預想的效果:取echo 一堆文件名的效果。

[root@MiWiFi-R3-srv ~]# echo $res
ls

[root@MiWiFi-R3-srv ~]# res=$(echo $(ls)) #替代方案
[root@MiWiFi-R3-srv ~]# echo $res
4.txt anaconda-ks.cfg a.txt B.txt c.txt D.txt nginx-1.10.3 nginx-1.10.3.tar.gz

~ 家目錄

[root@MiWiFi-R3-srv tmp]# cd ~
[root@MiWiFi-R3-srv ~]# pwd
/root

! 取非

[root@MiWiFi-R3-srv ~]# ls /dev/sda
sda sda1 sda2
[root@MiWiFi-R3-srv ~]# ls /dev/sda[0123]
/dev/sda1 /dev/sda2
[root@MiWiFi-R3-srv ~]# ls /dev/sda[!01]
/dev/sda2

! 歷史命令調用

[root@MiWiFi-R3-srv ~]# !343
hostname
MiWiFi-R3-srv

! 匹配最近一次歷史命令

[root@MiWiFi-R3-srv ~]# !ls
ls /dev/sda[!01]
/dev/sda2

! ls 帶空格 將命令的返回值取反

[root@MiWiFi-R3-srv ~]# echo ok
ok
[root@MiWiFi-R3-srv ~]# echo $? #上一條命令執行的結果,0代表執行成功,非0代表執行失敗
0
[root@MiWiFi-R3-srv ~]# ! echo ok #將結果取反
ok
[root@MiWiFi-R3-srv ~]# echo $?
1

0-255之間,0則為真,非0位假

@ 無特殊含義

# 註釋

$ 變量取值
$() 同``
${} 變量名的範圍

$[] 整數計算 echo $[2+3] - * / % 浮點數用 echo "scale=3; 10/3" | bc -l

[root@MiWiFi-R3-srv ~]# money=10
[root@MiWiFi-R3-srv ~]# echo $money
10
[root@MiWiFi-R3-srv ~]# echo 00000$money
0000010
[root@MiWiFi-R3-srv ~]# echo $money0000

[root@MiWiFi-R3-srv ~]# echo ${money}0000


100000

% 殺後臺進程 jobs號; 取模

^ 取非 和 ! 雷同

[root@MiWiFi-R3-srv ~]# ls /dev/sda[^01]
/dev/sda2
[root@MiWiFi-R3-srv ~]# ls /dev/sda[!01]
/dev/sda2

^ 替換

[root@MiWiFi-R3-srv ~]# systemctl restart network
[root@MiWiFi-R3-srv ~]# ^network^sshd^
systemctl restart sshd

& 後臺執行;&& 邏輯與

* 匹配任意長度字符串;計算乘法

() 在子進程中執行

[root@MiWiFi-R3-srv ~]# x=1
[root@MiWiFi-R3-srv ~]# (x=666)
[root@MiWiFi-R3-srv ~]# echo $x
1
[root@MiWiFi-R3-srv ~]#
[root@MiWiFi-R3-srv ~]# (x=666;echo $x)
666

- 減號;區間;cd -;

_ 無特殊含義

+ 加號 ;

= 賦值

| 管道; || 邏輯或

\\ 轉義;

{} 命令列表 ,注意括號內的開頭和結尾必須是空格{ ls; cd /; }

[] 字符通配,匹配括號內之一;

: 空命令 真值

[root@MiWiFi-R3-srv ~]# :
[root@MiWiFi-R3-srv ~]# echo $?
0

; 可以接多個命令:ls;pwd;echo 123;無論對錯,會一直執行到最後一條命令

"" 軟引 ''硬引

< 輸入重定向

> 輸出重定向

>> 追加

<< here document

>& 合併2和1輸出

, 枚舉分隔符

. source ; 當前目錄

/ 目錄分隔符

? 單個字符

回車 命令執行

* 通配符:任意字符


? 通配符:任一字符
[abc] 列表項之一
[^abc] 對列表取非 也可以使用範圍 [a-z] 代表aAbBcC...,[0-9]代表012345。。。
{} 循環列表

[root@MiWiFi-R3-srv test]# touch {1..3}{a..d}.txt
[root@MiWiFi-R3-srv test]# ls
1a.txt 1b.txt 1c.txt 1d.txt 2a.txt 2b.txt 2c.txt 2d.txt 3a.txt 3b.txt 3c.txt 3d.txt

控制變量名的範圍 echo ${AB}C

硬引用與軟引用

[root@MiWiFi-R3-srv test]# x=1
[root@MiWiFi-R3-srv test]# echo "$x" #雙引號的代表軟引用,引號內特殊字符有特殊意義,比如$,``等
1
[root@MiWiFi-R3-srv test]# echo '$x' #單引號代表硬引用,引號內所有字符都無特殊意義
$x

\\轉意

[root@MiWiFi-R3-srv test]# echo \\\\
\\
[root@MiWiFi-R3-srv test]# echo \\'
'


[root@MiWiFi-R3-srv test]# echo "'"
'

四 bash屬性

BASH SHELL 屬性
BASH中會存儲一些自身屬性的參數,啟用或關閉某一項功能
例如控制* .字符是否為通配
查看參數 set -o
關閉noglob參數
# set -o noglob
# ls *
ls: *: 沒有那個文件或目錄
# set +o noglob
ls *

固化設定
我們前面所學習的更改變量 屬性等等都是在內存中修改 機器重新啟動後就會恢復默認值
那麼怎麼固化這些設置 讓他們永久生效呢?
這就需要了解BASH兩種類型
1.登錄shell 2.非登錄shell
登錄shell
就是通過輸入用戶名 密碼後 或 su - 獲得的shell
非登錄shell 則是通過bash命令和腳本開啟的shell環境
那麼他們有什麼區別呢?和我們固化設定又有什麼關係呢?
我們知道在linux裡一切皆為文件,同樣,shell的屬性加載也是寫到文件裡的
在登陸時就會加載對應文件的內容來初始化shell環境,


非登錄與登錄區別就在於加載的文件不同 從而導致獲得的shell環境不同
我們看看登錄shell都加載了那些文件
--> /etc/profile
--> /etc/profile.d/*.sh
--> $HOME/.bash_profile
--> $HOME/.bashrc
--> /etc/bashrc
再看非登錄shell加載的文件
--> $HOME/.bashrc
--> /etc/bashrc
--> /etc/profile.d/*.sh
可見,非登錄shell加載的文件要少很多
那麼我們想要固化一個配置時在哪種登錄下生效,就顯而易見的知道該寫在哪個文件裡了
通常,我們會將環境變量設置在 $HOME/.bash_profile 中
如果不管哪種登錄都想使用的變量 就設置在 $HOME/.bashrc中

命令補齊TAB鍵
簡化輸入 提示 防止書寫錯誤


歷史記錄
上下鍵查
history 查詢 用!ID 調用
ctrl+r 輸入匹配

快捷鍵
CTRL+A 行首
CTRL+E 行尾
CTRL+U 刪除自光標到行首串
CTRL+K 刪除自光標到行尾串
CTRL+L 清屏


分享到:


相關文章: