1. 程式人生 > >linux入門系列7--管道符、重定向、環境變數

linux入門系列7--管道符、重定向、環境變數

前面文章我們學習了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命令將內容中的小寫字母替換為大寫字母。可以看到只是對讀取後的內容進行替換,對原檔案沒有影響。

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的值,然後切換到test使用者再次檢視$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,如果使用su - test 把shell環境也替換的話,將無法使用自定義的$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程式設計