前面文章我們學習了linux基礎命令,如果將不同命令組合使用則可以成倍提高工作效率。本文將學習重定向、管道符、通配符、轉義符、以及重要的環境變量相關知識,為后面的shell編程打下基礎。
一、IO重定向
前文我們已經講解了近60個linux常用命令,前文講解的查看當前目錄下有哪些文件的ls命令
[root@heimatengyun test]# ls
test1.txt test2.txt
執行命令后默認結果是輸出到電腦屏幕(顯示器)上的,但是如果我們想讓命令執行結果保存到文件中,方便以后需要的時候可以隨時查閱,我們該怎么做呢?這就要用到重定向的知識。
1.1 重定向概述
Linux shell重定向是指修改系統命令的默認執行方式,我們可以理解為“改變輸入和輸出的方向”,分為輸入重定向和輸出重定向。
既然重定向是改變默認的輸入輸出方向,那默認的輸入輸出方向又是什么呢?
相對程序而已,從鍵盤讀取用戶輸入數據供程序使用,也就是數據流從鍵盤到程序,這就是標準的輸入;程序運算產生的結果數據直接呈現在顯示器上,也就是數據流從程序到顯示器,這就是標準的輸出。默認的標準輸入、輸出如下圖:
將默認的從鍵盤讀取數據改為從文件讀取數據,也就是數據流從文件到程序,就是輸入重定向;程序運算產生的結果數據不顯示在顯示器上而是改為輸入到文件,也就是數據流從程序到文件,就是輸出重定向。
計算機的硬件設備有很多,常見的輸入設備有鍵盤、鼠標、麥克風、手寫板等,輸出設備有顯示器、投影儀、打印機等。 不過,在Linux中,標準輸入設備一般指鍵盤,標準輸出設備一般指顯示器。
前文提到過,Linux中一切皆文件,包括鍵盤、顯示器等輸入輸出設備在內的所有計算機硬件都是文件。為了表示和區分已經打開的文件,Linux會為每個文件分配一個ID,這個ID是一個整數,被稱為文件描述符(File Descriptor)。
與輸入輸出有關的文件描述符如下表:
文件描述符 | 文件名 | 類型 | 硬件 |
---|---|---|---|
0 | stdin | 標準輸入文件 | 硬盤 |
1 | stdout | 標準輸出文件 | 顯示器 |
2 | stderr | 標準錯誤輸出文件 | 顯示器 |
Linux程序在執行任何形式的IO操作時,都是在讀取或寫入一個文件描述符。一個文件描述符只是一個和打開的文件相關聯的整數,它被扣可能是一個硬盤上的普通文件、FIFO、管道、終端、鍵盤、顯示器,甚至是一個網絡連接。stdin、stdout、stderr默認都是打開的,在重定向的過程中,0、1、2這三個文件描述符可以直接使用。
1.2 重定向分類
重定向分為輸入和輸出重定向。簡言之,輸入重定向就是把文件導入到命令,輸出重定向則是把原本要輸出到屏幕的信息寫入到指定文件中。平時工作中,相對于輸入重定向,輸出重定向使用頻率更高,因此又將輸出重定向細分為標準輸出重定向和錯誤輸出重定向,輸出重定向又分為:清空寫入和追加兩種模式。
關于標準輸出和錯誤輸出請看下面示例:
[root@heimatengyun ~]# ls test/
test1.txt test2.txt
[root@heimatengyun ~]# ls xxx
ls: cannot access xxx: No such file or directory
用ls命令查看制定目錄下的文件信息,如果文件夾存在且文件夾下有內容將輸出文件所有者、所屬組、文件大小等信息,也就是ls命令的標準輸出信息。但是如果查看一個不存在的文件夾,則提示文件不存在的報錯信息,也就是ls命令的錯誤輸出信息。如果要把上邊原本輸出到屏幕的信息直接寫入到文件中而不是顯示到屏幕,就要區別對待這兩種輸出信息。
1.2.1 輸入重定向
輸入重定向相關的符號和作用如下表
符號 | 作用 |
---|---|
命令 < 文件 | 將文件作為命令的標準輸入 |
命令 << 分界符 | 從標準輸入中讀入,直到遇到分解符才停止 |
命令 < 文件1 > 文件2 | 將文件1作為命令的標準輸入并將標準輸出到文件2 |
輸入重定向相對來說用的很少,輸入重定向的作用是將文件直接導入到命令中。/etc/passwd文件存儲了系統用戶信息,一行記錄一個用戶。以下示例演示通過輸入重定向將此文件導入到wc命令,統計用戶個數。
[root@heimatengyun test]# wc -l < /etc/passwd
39
1.2.2 輸出重定向
輸出重定向用到的符號和作用如下表
符號 | 作用 |
---|---|
命令 1> 文件 | 將標準輸出重定向到文件中(清空原有文件數據),1可以省略 |
命令 2> 文件 | 將錯誤輸出重定向到文件中(清空原有文件數據) |
命令 1>> 文件 | 將標準輸出重定向到文件中(追加到原有內容后面),1可以省略 |
命令 2>> 文件 | 將錯誤輸出重定向到文件中(追加到原有內容后面) |
命令 &>> 文件 | 將標準輸出和錯誤輸出共同寫入到文件中(追加到原有內容后面) |
命令 >> 文件 2>&1 | 同上條命令:命令 &>> 文件 |
對于重定向中的標準輸出模式,一般省略文件描述符1不寫,而錯誤輸出模式的文件描述符2是必須要寫的。
1.2.3 輸出重定向案例
通過man命令查看ls命令的使用方法,并將輸出信息重定向到ls.txt文件中,然后就可以使用cat命令查看ls.txt文件的信息。
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# man ls > ls.txt
[root@heimatengyun test]# ls
ls.txt test1.txt test2.txt
[root@heimatengyun test]# cat ls.txt
LS(1) User Commands LS(1)
NAME
ls - list directory contents
SYNOPSIS
ls [OPTION]... [FILE]...
DESCRIPTION
...省略部分內容
接下來我們演示清空寫入和追加寫入的區別,先通過覆蓋寫入模式向ls.txt文件(原本有ls的幫助信息內容)寫入一行數據,查看內容變化,然后再通過追加寫入模式向文件寫入一次數據,再查看文件內容的變化
[root@heimatengyun test]# echo "wellcome" > ls.txt
[root@heimatengyun test]# cat ls.txt
wellcome
[root@heimatengyun test]# echo "write message again" >> ls.txt
[root@heimatengyun test]# cat ls.txt
wellcome
write message again
可以看到覆蓋模式將清空文件原有內容,追加模式則在原有內容后面添加數據。
1.2.4 標準輸出和錯誤輸出區別
雖然都是重定向技術,不同命令的標準輸出和錯誤輸出還是有區別的。如果一個命令執行成功,通過標準輸出到文件是沒有問題的,但是如果要錯誤輸出重定向到文件是不會成功的,依舊會顯示信息到屏幕。反之,如果一個命令執行失敗,通過錯誤輸出到文件是沒有問題的,但是如果要輸出到標準輸出是不會成功的,依舊會顯示到屏幕。
通過ls命令查看一個已經存在的文件,并將信息重定向到ls.txt文件,查看該文件可以看到成功存入信息。將其錯誤輸出重定向到ls-err.txt文件,由于查看的該文件存在,沒有錯誤信息所以看到查詢成功的文件信息依然顯示在了屏幕上,而錯誤重定向的文件里沒有內容。
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# ls -l test1.txt
-rw-r--r--. 1 root root 0 Nov 30 15:34 test1.txt
[root@heimatengyun test]# ls -l test1.txt > ls.txt
[root@heimatengyun test]# ls
ls.txt test1.txt test2.txt
[root@heimatengyun test]# cat ls.txt
-rw-r--r--. 1 root root 0 Nov 30 15:34 test1.txt
[root@heimatengyun test]# ls -l test1.txt 2> ls-err.txt
-rw-r--r--. 1 root root 0 Nov 30 15:34 test1.txt
[root@heimatengyun test]# ls
ls-err.txt ls.txt test1.txt test2.txt
[root@heimatengyun test]# cat ls-err.txt
二、管道符
通過管道符可以把很多命令組合起來,提高工作效率。簡言之管道符的作用就是:把前一個命令原本要輸出到屏幕的標準正常數據當作后一個命令的標準輸入。
管道符用|表示,使用格式為:命令A|命令B|命令C...
- 案例1:統計被禁止登陸系統的用戶數
[root@heimatengyun test]# grep "/sbin/nologin" /etc/passwd |wc -l
34
通過“linux入門系列5--新手必會的linux命令”介紹的grep命令匹配/etc/passwd文件中的關鍵字“/sbin/nologin”查找出被限制登陸系統的用戶,并將匹配結果輸入到wc命令,統計匹配到的行數,即為被限制登陸系統的用戶數。
- 案例2:將文件內容中的小寫字母替換為大寫字母輸出
[root@heimatengyun test]# cat test1.txt
wellcome
[root@heimatengyun test]# cat test1.txt |tr [a-z] [A-Z]
WELLCOME
[root@heimatengyun test]# cat test1.txt
wellcome
通過cat命令讀取test1.txt文件內容并導入到tr命令,通過tr命令將內容中的小寫字母替換為大寫字母??梢钥吹街皇菍ψx取后的內容進行替換,對原文件沒有影響。
tr命令作用是替換文本文件中的字符,格式為:tr [原始字符] [目標字符]
很多時候想要快速地替換文本中的一些詞匯,如果手工替換,難免工作量巨大,尤其是需要處理大批量內容的時候。這時tr命令就可以派上用場,通過管道符將文本內容傳遞給它進行替換操作即可。
ps:前文講了近60個Linux命令,命令太多不可能一一涵蓋,其余的命令將根據場景需求逐步以案例的形式分散到各文中進行演示。
三、通配符
通配符的概念在很多語言中都存在,比如java、c#等,其作用就是模糊匹配。
假設你在電腦上存放了很多小電影,某一天突然想看某位老師的電影作品,但是由于文件太多以至于記不清楚電影文件的名稱了,只是依稀記得文件名包含了幾個關鍵字,這時候你怎么快速找到對應的文件呢?
通配符就是面對這種場景而生,熟練使用通配符,再多電影都不迷路。通配符顧名思義就是通用的匹配信息的符號,主要包含以下幾個:
符號 | 意義 |
---|---|
* | 匹配0個和多個字符 |
? | 匹配單個字符 |
[0-9] | 匹配0~9之間的單個數字字符 |
[123] | 匹配1、2、3這三個指定數字中的任意一個數字 |
[abc] | 匹配a、b、c三個字符中的任意一個字符 |
- 案例1:匹配文件名以test開頭的所有文件
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# ls -l test*
-rw-r--r--. 1 root root 9 Nov 30 20:43 test1.txt
-rw-r--r--. 1 root root 0 Nov 30 15:34 test2.txt
- 案例2:匹配文件名最后一位為1或3的所有文件
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# ls -l test[13].txt
-rw-r--r--. 1 root root 9 Nov 30 20:43 test1.txt
四、轉義符
“linux入門系列5--新手必會的linux命令”提到,人和Linux內核之間的交互是通過在shell終端中執行相關命令來實現的,為了能更好地理解用戶的表達,除了通配符、管道符,shell解釋器還提供了特別豐富的轉義字符來處理用戶輸入的特殊數據。
本文只抽取幾個常用的通配符進行講解,轉義符及對應的功能如下:
轉義符 | 作用 |
---|---|
\ | 反斜杠,使后邊的一個變量變為單純的字符串 |
'' | 單引號,轉義其中所有的變量為單純的字符串 |
"" | 雙引號,保留其中的變量屬性,不進行轉義處理 |
`` | 反引號,把其中的命令執行后返回結果 |
- 案例1:輸出美元$表示的價格
[root@heimatengyun test]# PRICE=99
[root@heimatengyun test]# echo "the price is $PRICE"
the price is 99
[root@heimatengyun test]# echo "the price is $$PRICE"
the price is 12395PRICE
定義PRICE變量保存價格,然后通過echo命令輸出,發現輸出的不是預期結果。原因是Linux中$表示變量,$$則有特殊的作用,表示當前程序的進程ID號。這時就需要反斜杠來進行轉義,去除其特殊功能,將這個提取符轉義為單純的文本。
[root@heimatengyun test]# echo "the price is \$$PRICE"
the price is $99
- 案例2:將命令執行結果值賦值給變量并輸出
[root@heimatengyun test]# uname -a
Linux heimatengyun 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@heimatengyun test]# MYSYS=`uname -a`
[root@heimatengyun test]# echo $MYSYS
Linux heimatengyun 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
用uname命令查看當前操作系統信息,并賦值給變量MYSYS,然后輸出變量值。更多變量相關知識將在下一篇文章中詳細介紹,此處主要掌握反引號這個轉義符。
五、環境變量
變量是計算機系統用于保存可變值的數據類型,在Linux系統中,變量名稱一般是大寫的,這是一種約定俗成的規范。直接通過變量名即可獲得對應的變量值。
環境變量是一種特殊的變量,是操作系統要正常運行的前提,數百個環境變量協同工作才使得操作系統能正常為用戶提供服務。然而我們沒有必要去全部學習和掌握所有數百個環境變量,只需要學習一部分常用的即可。
5.1 查看環境變量之env命令
一般通過env命令查看環境變量名
[root@heimatengyun test]# env
XDG_SESSION_ID=2
HOSTNAME=heimatengyun
SELINUX_ROLE_REQUESTED=
TERM=linux
SHELL=/bin/bash
...省略部分內容
用echo命令查詢環境變量值
[root@heimatengyun test]# echo $SHELL
/bin/bash
Linux作為一個多用戶多任務操作系統,能夠為每個用戶提供獨立的工作環境,因此,一個相同的變量會因為用戶身份的不同而具有不同的值。
案例:使用不同用戶查看HOME環境變量的值
[root@heimatengyun ~]# echo $HOME
/root
[root@heimatengyun ~]# su - test
Last login: Sat Nov 30 22:39:50 CST 2019 on pts/0
[test@heimatengyun ~]$ echo $HOME
/home/test
[test@heimatengyun ~]$ exit
logout
[root@heimatengyun ~]#
案例中先使用root用戶查看HOME,從試驗結果上看相同環境變量值卻是不一樣的。
注意:關于用戶切換命令su的用法,su test和su - test是有非常大區別的,如果不加-表示只是切換用戶不切換shell環境,如果加上-則表示連同shell環境一起替換,此處無論是否切換shell環境,兩個不同用戶的$HOME值都不一樣。
5.2 設置環境變量之export命令
變量是由固定的變量名與用戶或系統設置的變量值兩部分注冊,因此我們完全可以根據工作需要自行創建變量。
以下案例演示創建一個名稱為$MYDIR,值為/etc/profile.d/ 目錄的自定義變量,這樣我們只需要通過該變量,就可以很方便的進入到值對應的目錄。
[root@heimatengyun test]# MYDIR=/etc/profile.d/
[root@heimatengyun test]# echo $MYDIR
/etc/profile.d/
[root@heimatengyun test]# pwd
/root/test
[root@heimatengyun test]# cd $MYDIR
[root@heimatengyun profile.d]# pwd
/etc/profile.d
此時創建的變量$MYDIR不具有全局性,作用范圍有限,默認情況下不能被其他用戶使用。
[root@heimatengyun ~]# su test
[test@heimatengyun root]$ echo $MYDIR
[test@heimatengyun root]$ exit
exit
可以看到切換到test用戶后,該變量沒有值,并且通過env命令查看也未查到該變量。
如果要想讓其他用戶也可以使用該變量,則需要用export命令,將其提升為全局變量。注意export命令后的變量名不加$。
[root@heimatengyun ~]# export MYDIR
[root@heimatengyun ~]# env
XDG_SESSION_ID=2
HOSTNAME=heimatengyun
SELINUX_USE_CURRENT_RANGE=
MYDIR=/etc/profile.d/
...省略部分內容
通過env命令也可以查到該變量,此時我們在切換到test用戶查看是否可以使用
[root@heimatengyun ~]# su test
[test@heimatengyun root]$ echo $MYDIR
/etc/profile.d/
[test@heimatengyun root]$ exit
exit
注意:再次強調一下,su test和su - test是有非常大區別的,如果不加-表示只是切換用戶不切換shell環境。本例只是切換用戶到test并沒切換環境所以可以使用
MYDIR環境變量。
5.3 常用的環境變量
下表列舉幾個重要常用的環境變量
變量名稱 | 作用 |
---|---|
HOME | 用戶家目錄 |
SHELL | 用戶在使用的shell解釋器名稱 |
HISTSIZE | 輸出的歷史命令記錄條數 |
HISTFILESIZE | 保存的歷史命令記錄條數 |
LANG | 系統語言、語系名稱 |
PATH | 定義解釋器搜索用戶執行命令的路徑 |
5.4 命令執行流程
Linux系統中一切皆文件,Linux命令也不例外。當用戶執行一條命令之后,Linux系統到底發生了什么事情呢?
簡單來說,命令在Linux中的執行分為以下4個步驟
(1)判斷用戶是否以絕對路徑或相對路徑的方式輸入命令,如果是則直接執行,不是則進行第二步
(2)檢查用戶輸入的命令是否有別名命令,如果有別名找到原命令,如果無則進行第三步
(3)bash解釋器判斷用戶輸入的是內部命令還是外部命令,如是內部命令則直接執行,外部命令則進行第四步
(4)系統在PATH環境變量中查找用戶輸入的命令,找到文件后執行命令。
簡單理解就是用戶通過shell輸入命令,shell解釋器查找對應的命令文件并執行命令。
注意:思考一下一個經典的問題,能否將當前目錄(.)添加到環境變量PATH中呢?
盡管可以將當前目錄(.)添加到PATH變量中,使得在某些情況下可以讓用戶免去輸入命令路徑的麻煩,但是,這樣存在很大的安全風險。假如黑客在常用的公用目錄/tmp下存放一個與ls或cd等命令同名的病毒文件,而用戶恰巧又在公共目錄中執行了這些命令,那就很可能中招了。
了解linux命令執行流程后,當接手一臺Linux系統后,在執行命令前先檢查PATH變量中是否有可以的目錄,這是一個很好的習慣。
至此,已經學習了大部分Linux命令,知識積累的差不多了,下一篇文章我們將綜合前面學到的知識,正式進入shell編程。