LINUX第三节课
管道符、重定向與環境變量
輸入輸出重定向
簡而言之,輸入重定向是指把文件導入到命令中,而輸出重定向則是指把原本要輸出到屏幕的數據信息寫入到指定文件中。又將輸出重定向分為了標準輸出重定向和錯誤輸出重定向兩種不同的技術,以及清空寫入與追加寫入兩種模式。
標準輸入重定向(STDIN,文件描述符為0):默認從鍵盤輸入,也可從其他文件或命令中輸入。
標準輸出重定向(STDOUT,文件描述符為1):默認輸出到屏幕。
錯誤輸出重定向(STDERR,文件描述符為2):默認輸出到屏幕。
對于輸入重定向來講,用到的符號及其作用如表3-1所示。
命令 < 文件 將文件作為命令的標準輸入
命令 << 分界符 從標準輸入中讀入,直到遇見分界符才停止
命令 < 文件1 > 文件2 將文件1作為命令的標準輸入并將標準輸出到文件2
符號 作用
命令 > 文件 將標準輸出重定向到一個文件中(清空原有文件的數據)
命令 2> 文件 將錯誤輸出重定向到一個文件中(清空原有文件的數據)
命令 >> 文件 將標準輸出重定向到一個文件中(追加到原有內容的后面)
命令 2>> 文件 將錯誤輸出重定向到一個文件中(追加到原有內容的后面)
命令 >> 文件 2>&1
或
命令 &>> 文件 將標準輸出與錯誤輸出共同寫入到文件中(追加到原有內容的后面)
[root@linuxprobe ~]# man bash > readme.txt
[root@linuxprobe ~]# cat readme.txt
BASH(1) General Commands Manual BASH(1)
NAME
bash - GNU Bourne-Again SHell
SYNOPSIS
輸入重定向相對來說有些冷門,在工作中遇到的概率會小一點。輸入重定向的作用是把文件直接導入到命令中。接下來使用輸入重定向把readme.txt文件導入給wc -l命令,統計一下文件中的內容行數。
wc -l < readme.txt
管道命令符
[oot@linuxprobe ~]# ls -l /etc/ | more
total 1400
drwxr-xr-x. 3 root root 97 Jul 10 17:26 abrt
-rw-r–r--. 1 root root 16 Jul 10 17:36 adjtime
-rw-r–r--. 1 root root 1518 Jun 7 2013 aliases
-rw-r–r--. 1 root root 12288 Jul 10 09:38 aliases.db
drwxr-xr-x. 2 root root 49 Jul 10 17:26 alsa
drwxr-xr-x. 2 root root 4096 Jul 10 17:31 alternatives
-rw-------. 1 root root 541 Jan 28 2017 anacrontab
-rw-r–r--. 1 root root 55 Jan 29 2017 asound.conf
-rw-r–r--. 1 root root 1 Jan 29 2017 at.deny
drwxr-xr-x. 2 root root 31 Jul 10 17:27 at-spi2
drwxr-x—. 3 root root 41 Jul 10 17:26 audisp
drwxr-x—. 3 root root 79 Jul 10 17:37 audit
drwxr-xr-x. 4 root root 94 Jul 10 17:26 avahi
[root@linuxprobe ~]# grep “/sbin/nologin” /etc/passwd | wc -l
[root@linuxprobe ~]# echo “linuxprobe” | passwd --stdin root
Changing password for user root.
passwd: all authentication tokens updated successfully.
[root@linuxprobe ~]# echo “Content” | mail -s “Subject” linuxprobe
[root@linuxprobe ~]# su - linuxprobe
Last login: Fri Jul 10 09:44:07 CST 2017 on :0
[linuxprobe@linuxprobe ~]$ mail
Heirloom Mail version 12.5 7/5/10. Type ? for help.
“/var/spool/mail/linuxprobe”: 1 message 1 new
**>N 1 root Sun Aug 30 17:33 18/578 “Subject”
root@linuxprobe ~]# mail -s “Readme” root@linuxprobe.com << over
I think linux is very practical
I hope to learn more
can you teach me ?
over
命令行的通配符
通配符就是通用的匹配信息的符號,比如星號()代表匹配零個或多個字符,問號(?)代表匹配單個字符,中括號內加上數字[0-9]代表匹配0~9之間的單個數字的字符,而中括號內加上字母[abc]則是代表匹配a、b、c三個字符中的任意一個字符。
[root@linuxprobe ~]# ls -l /dev/sda
brw-rw----. 1 root disk 8, 0 May 4 15:55 /dev/sda
brw-rw----. 1 root disk 8, 1 May 4 15:55 /dev/sda1
brw-rw----. 1 root disk 8, 2 May 4 15:55 /dev/sda2
[root@linuxprobe ~]# ls -l /dev/sda[0-9]
brw-rw----. 1 root disk 8, 1 May 4 15:55 /dev/sda1
brw-rw----. 1 root disk 8, 2 May 4 15:55 /dev/sda2
[root@linuxprobe ~]# ls -l /dev/sda[135]
brw-rw----. 1 root disk 8, 1 May 4 15:55 /dev/sda1
常用的轉義字符
4個最常用的轉義字符如下所示。
反斜杠(\):使反斜杠后面的一個變量變為單純的字符串。
單引號(’’):轉義其中所有的變量為單純的字符串。
雙引號(""):保留其中的變量屬性,不進行轉義處理。
反引號(``):把其中的命令執行后返回結果。
我們先定義一個名為PRICE的變量并賦值為5,然后輸出以雙引號括起來的字符串與變量信息:
[root@linuxprobe ~]# PRICE=5
[root@linuxprobe ~]# echo “Price is $PRICE”
Price is 5
接下來,我們希望能夠輸出“Price
is 5?”,即價格是5美元的字符串內容,但碰巧美元符號與變量提取符號合并后的5*”,即價格是5美元的字符串內容,但碰巧美元符號與變量提取符號合并后的5?”,即價格是5美元的字符串內容,但碰巧美元符號與變量提取符號合并后的$作用是顯示當前程序的進程ID號碼,于是*命令執行后輸出的內容并不是我們所預期的:
[root@linuxprobe ~]# echo "Price is $PRICE"Priceis3767PRICE要想讓第一個“PRICE" Price is 3767PRICE 要想讓第一個“PRICE"Priceis3767PRICE要想讓第一個“”乖乖地作為美元符號,那么就需要使用反斜杠(\)來進行轉義,將這個命令提取符轉義成單純的文本,去除其特殊功能
[root@linuxprobe ~]# echo “Price is $$PRICE”
Price is $5
如果只需要某個命令的輸出值時,可以像命令這樣,將命令用反引號括起來,達到預期的效果。例如,將反引號與uname -a命令結合,然后使用echo命令來查看本機的Linux版本和內核信息
[root@linuxprobe ~]# echo uname -a
Linux linuxprobe.com 3.10.0-123.el7.x86_64 #1 SMP Mon May 5 11:16:57 EDT 2017
x86_64 x86_64 x86_64 GNU/Linux
重要的環境變量
變量是計算機系統用于保存可變值的數據類型。在Linux系統中,變量名稱一般都是大寫的,這是一種約定俗成的規范。我們可以直接通過變量名稱來提取到對應的變量值。Linux系統中的環境變量是用來定義系統運行環境的一些參數,比如每個用戶不同的家目錄、郵件存放位置等。
在Linux系統中一切都是文件,Linux命令也不例外。那么,在用戶執行了一條命令之后,Linux系統中到底發生了什么事情呢?簡單來說,命令在Linux中的執行分為4個步驟。
第1步:判斷用戶是否以絕對路徑或相對路徑的方式輸入命令(如/bin/ls),如果是的話則直接執行。
第2步:Linux系統檢查用戶輸入的命令是否為“別名命令”,即用一個自定義的命令名稱來替換原本的命令名稱。可以用alias命令來創建一個屬于自己的命令別名,格式為“alias 別名=命令”。若要取消一個命令別名,則是用unalias命令,格式為“unalias 別名”。我們之前在使用rm命令刪除文件時,Linux系統都會要求我們再確認是否執行刪除操作,其實這就是Linux系統為了防止用戶誤刪除文件而特意設置的rm別名命令,接下來我們把它取消掉:
第3步:Bash解釋器判斷用戶輸入的是內部命令還是外部命令。內部命令是解釋器內部的指令,會被直接執行;而用戶在絕大部分時間輸入的是外部命令,這些命令交由步驟4繼續處理。可以使用“type命令名稱”來判斷用戶輸入的命令是內部命令還是外部命令。
第4步:系統在多個路徑中查找用戶輸入的命令文件,而定義這些路徑的變量叫作PATH,可以簡單地把它理解成是“解釋器的小助手”,作用是告訴Bash解釋器待執行的命令可能存放的位置,然后Bash解釋器就會乖乖地在這些位置中逐個查找。PATH是由多個路徑值組成的變量,每個路徑值之間用冒號間隔,對這些路徑的增加和刪除操作將影響到Bash解釋器對Linux命令的查找。
[root@linuxprobe ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 86: …t@linuxprobe ~]#? PATH=PATH:/root/bin
[root@linuxprobe ~]# echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/root/bin
Linux系統中最重要的10個環境變量
變量名稱 作用
HOME 用戶的主目錄(即家目錄)
SHELL 用戶在使用的Shell解釋器名稱
HISTSIZE 輸出的歷史命令記錄條數
HISTFILESIZE 保存的歷史命令記錄條數
MAIL 郵件保存路徑
LANG 系統語言、語系名稱
RANDOM 生成一個隨機數字
PS1 Bash解釋器的提示符
PATH 定義解釋器搜索用戶執行命令的路徑
EDITOR 用戶默認的文本編輯器
Linux作為一個多用戶多任務的操作系統,能夠為每個用戶提供獨立的、合適的工作運行環境,因此,一個相同的變量會因為用戶身份的不同而具有不同的值。例如,我們使用下述命令來查看HOME變量在不同用戶身份下都有哪些值
[root@linuxprobe ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 31: …t@linuxprobe ~]#? su - linuxprob… echo KaTeX parse error: Expected 'EOF', got '#' at position 141: …xprobe workdir]#? su linuxprobe … cd WORKDIR[linuxprobe@linuxprobe]WORKDIR [linuxprobe@linuxprobe ~]WORKDIR[linuxprobe@linuxprobe?] echo WORKDIR[linuxprobe@linuxprobe]WORKDIR [linuxprobe@linuxprobe ~]WORKDIR[linuxprobe@linuxprobe?] exit
[root@linuxprobe ~]# export WORKDIR
[root@linuxprobe ~]# su linuxprobe
Last login: Fri Mar 20 21:52:10 CST 2017 on pts/0
[linuxprobe@linuxprobe ~]$ cd WORKDIR[linuxprobe@linuxprobeworkdir]WORKDIR [linuxprobe@linuxprobe workdir]WORKDIR[linuxprobe@linuxprobeworkdir] pwd
/home/workdir
這樣的變量不具有全局性,作用范圍也有限,默認情況下不能被其他用戶使用。如果工作需要,可以使用export命令將其提升為全局變量,這樣其他用戶也就可以使用它了:
[root@linuxprobe workdir]# su linuxprobe
Last login: Fri Mar 20 20:52:10 CST 2017 on pts/0
[linuxprobe@linuxprobe ~]$ cd WORKDIR[linuxprobe@linuxprobe]WORKDIR [linuxprobe@linuxprobe ~]WORKDIR[linuxprobe@linuxprobe?] echo WORKDIR[linuxprobe@linuxprobe]WORKDIR [linuxprobe@linuxprobe ~]WORKDIR[linuxprobe@linuxprobe?] exit
[root@linuxprobe ~]# export WORKDIR
[root@linuxprobe ~]# su linuxprobe
Last login: Fri Mar 20 21:52:10 CST 2017 on pts/0
[linuxprobe@linuxprobe ~]$ cd WORKDIR[linuxprobe@linuxprobeworkdir]WORKDIR [linuxprobe@linuxprobe workdir]WORKDIR[linuxprobe@linuxprobeworkdir] pwd
/home/workdir
Vim編輯器與Shell命令腳本
Linux系統中一切都是文件,而配置一個服務就是在修改其配置文件的參數
命令模式:控制光標移動,可對文本進行復制、粘貼、刪除和查找等工作。
輸入模式:正常的文本錄入。
末行模式:保存或退出文檔,以及設置編輯環境。
Vim中常用的命令
命令 作用
dd 刪除(剪切)光標所在整行
5dd 刪除(剪切)從光標處開始的5行
yy 復制光標所在整行
5yy 復制從光標處開始的5行
n 顯示搜索命令定位到的下一個字符串
N 顯示搜索命令定位到的上一個字符串
u 撤銷上一步的操作
p 將之前刪除(dd)或復制(yy)過的數據粘貼到光標后面
末行模式主要用于保存或退出文件,以及設置Vim編輯器的工作環境,還可以讓用戶執行外部的Linux命令或跳轉到所編寫文檔的特定行數。要想切換到末行模式,在命令模式中輸入一個冒號就可以了。末行模式中可用的命令如表4-2所示。
表4-2 末行模式中可用的命令
命令 作用
:w 保存
:q 退出
:q! 強制退出(放棄對文檔的修改內容)
:wq! 強制保存退出
:set nu 顯示行號
:set nonu 不顯示行號
:命令 執行該命令
:整數 跳轉到該行
續表
命令 作用
😒/one/two 將當前光標所在行的第一個one替換成two
😒/one/two/g 將當前光標所在行的所有one替換成two
:%s/one/two/g 將全文中的所有one替換成two
?字符串 在文本中從下至上搜索該字符串
/字符串 在文本中從上至下搜索該字符串
編寫簡單文檔
可以分別使用a、i、o三個鍵從命令模式切換到輸入模式。其中,a鍵與i鍵分別是在光標后面一位和光標當前位置切換到輸入模式,而o鍵則是在光標的下面再創建一個空行,此時可敲擊a鍵進入到編輯器的輸入模式
進入輸入模式后,可以隨意輸入文本內容,Vim編輯器不會把您輸入的文本內容當作命令而執行,
在編寫完之后,想要保存并退出,必須先敲擊鍵盤Esc鍵從輸入模式返回命令模式,如圖4-5所示。然后再輸入:wq!切換到末行模式才能完成保存退出操作
當在末行模式中輸入:wq!命令時,就意味著強制保存并退出文檔。然后便可以用cat命令查看保存后的文檔內容了
配置主機名稱
為了便于在局域網中查找某臺特定的主機,或者對主機進行區分,除了要有IP地址外,還要為主機配置一個主機名,主機之間可以通過這個類似于域名的名稱來相互訪問。在Linux系統中,主機名大多保存在/etc/hostname文件中,接下來將/etc/hostname文件的內容修改為“linuxprobe.com”,步驟如下。
第1步:使用Vim編輯器修改“/etc/hostname”主機名稱文件。
第2步:把原始主機名稱刪除后追加“linuxprobe.com”。注意,使用Vim編輯器修改主機名稱文件后,要在末行模式下執行:wq!命令才能保存并退出文檔。
第3步:保存并退出文檔,然后使用hostname命令檢查是否修改成功。
[root@linuxprobe ~]# vim /etc/hostname
linuxprobe.com
現在有一個名稱為ifcfg-eno16777736的網卡設備,我們將其配置為開機自啟動,并且IP地址、子網、網關等信息由人工指定,其步驟應該如下所示。
第1步:首先切換到/etc/sysconfig/network-scripts目錄中(存放著網卡的配置文件)。
第2步:使用Vim編輯器修改網卡文件ifcfg-eno16777736,逐項寫入下面的配置參數并保存退出。由于每臺設備的硬件及架構是不一樣的,因此請讀者使用ifconfig命令自行確認各自網卡的默認名稱。
設備類型:TYPE=Ethernet
地址分配模式:BOOTPROTO=static
網卡名稱:NAME=eno16777736
是否啟動:ONBOOT=yes
IP地址:IPADDR=192.168.10.10
子網掩碼:NETMASK=255.255.255.0
網關地址:GATEWAY=192.168.10.1
DNS地址:DNS1=192.168.10.1
第3步:重啟網絡服務并測試網絡是否聯通。
[root@linuxprobe ~]# cd /etc/sysconfig/network-scripts/
[root@linuxprobe network-scripts]# vim ifcfg-eno16777736
TYPE=Ethernet
BOOTPROTO=static
NAME=eno16777736
ONBOOT=yes
IPADDR=192.168.10.10
NETMASK=255.255.255.0
GATEWAY=192.168.10.1
DNS1=192.168.10.1
執行重啟網卡設備的命令(在正常情況下不會有提示信息),然后通過ping命令測試網絡能否聯通。由于在Linux系統中ping命令不會自動終止,因此需要手動按下Ctrl-c鍵來強行結束進程。
[root@linuxprobe network-scripts]# ping 192.168.10.10
PING 192.168.10.10 (192.168.10.10) 56(84) bytes of data.
64 bytes from 192.168.10.10: icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from 192.168.10.10: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 192.168.10.10: icmp_seq=3 ttl=64 time=0.059 ms
64 bytes from 192.168.10.10: icmp_seq=4 ttl=64 time=0.097 ms
配置Yum軟件倉庫
本書前面講到,Yum軟件倉庫的作用是為了進一步簡化RPM管理軟件的難度以及自動分析所需軟件包及其依賴關系的技術。可以把Yum想象成是一個碩大的軟件倉庫,里面保存有幾乎所有常用的工具,而且只需要說出所需的軟件包名稱,系統就會自動為您搞定一切。
既然要使用Yum軟件倉庫,就要先把它搭建起來,然后將其配置規則確定好才行。鑒于第6章才會講解Linux的存儲結構和設備掛載操作,所以我們當前還是將重心放到Vim編輯器的學習上。如果遇到看不懂的參數也不要緊,后面章節會單獨講解。搭建并配置Yum軟件倉庫的大致步驟如下所示。
第1步:進入到/etc/yum.repos.d/目錄中(因為該目錄存放著Yum軟件倉庫的配置文件)。
第2步:使用Vim編輯器創建一個名為rhel7.repo的新配置文件(文件名稱可隨意,但后綴必須為.repo),逐項寫入下面加粗的配置參數并保存退出(不要寫后面的中文注釋)。
[rhel-media] :Yum軟件倉庫唯一標識符,避免與其他倉庫沖突。
name=linuxprobe:Yum軟件倉庫的名稱描述,易于識別倉庫用處。
baseurl=file:///media/cdrom:提供的方式包括FTP(ftp://…)、HTTP(http://…)、本地(file:///…)。
enabled=1:設置此源是否可用;1為可用,0為禁用。
gpgcheck=1:設置此源是否校驗文件;1為校驗,0為不校驗。
gpgkey=file:///media/cdrom/RPM-GPG-KEY-redhat-release:若上面參數開啟校驗,那么請指定公鑰文件地址。
第3步:按配置參數的路徑掛載光盤,并把光盤掛載信息寫入到/etc/fstab文件中。
第4步:使用“yum install httpd -y”命令檢查Yum軟件倉庫是否已經可用。
進入/etc/yum.repos.d目錄中后創建Yum配置文件
[root@linuxprobe ~]# cd /etc/yum.repos.d/
[root@linuxprobe yum.repos.d]# vim rhel7.repo
[rhel7]
name=rhel7
baseurl=file:///media/cdrom
enabled=1
gpgcheck=0
創建掛載點后進行掛載操作,并設置成開機自動掛載(詳見第6章)。嘗試使用Yum軟件倉庫來安裝Web服務,出現Complete!則代表配置正確
[root@linuxprobe yum.repos.d]# mkdir -p /media/cdrom
[root@linuxprobe yum.repos.d]# mount /dev/cdrom /media/cdrom
mount: /dev/sr0 is write-protected, mounting read-only
[root@linuxprobe yum.repos.d]# vim /etc/fstab
/dev/cdrom /media/cdrom iso9660 defaults 0 0
[root@linuxprobe ~]# yum install httpd
Loaded plugins: langpacks, product-id, subscription-manager
………………省略部分輸出信息………………
Dependencies Resolved
Package Arch Version Repository Size
Installing:
httpd x86_64 2.4.6-17.el7 rhel 1.2 M
Installing for dependencies:
apr x86_64 1.4.8-3.el7 rhel 103 k
apr-util x86_64 1.5.2-6.el7 rhel 92 k
httpd-tools x86_64 2.4.6-17.el7 rhel 77 k
mailcap noarch 2.1.41-2.el7 rhel 31 k
Transaction Summary
Install 1 Package (+4 Dependent packages)
Total download size: 1.5 M
Installed size: 4.3 M
Is this ok [y/d/N]: y
………………省略部分輸出信息………………
Complete
編寫Shell腳本
查看SHELL變量可以發現當前系統已經默認使用Bash作為命令行終端解釋器
[root@linuxprobe ~]# echo $SHELL
/bin/bash
編寫Shell腳本
將Shell終端解釋器當作人與計算機硬件之間的“翻譯官”,它作為用戶與Linux系統內部的通信媒介,除了能夠支持各種變量與參數外,還提供了諸如循環、分支等高級編程語言才有的控制結構特性。要想正確使用Shell中的這些功能特性,準確下達命令尤為重要。Shell腳本命令的工作方式有兩種:交互式和批處理。
交互式(Interactive):用戶每輸入一條命令就立即執行。
批處理(Batch):由用戶事先編寫好一個完整的Shell腳本,Shell會一次性執行腳本中諸多的命令。
在Shell腳本中不僅會用到前面學習過的很多Linux命令以及正則表達式、管道符、數據流重定向等語法規則,還需要把內部功能模塊化后通過邏輯語句進行處理,最終形成日常所見的Shell腳本。
編寫簡單的腳本
上文指的是一個高級Shell腳本的編寫原則,其實使用Vim編輯器把Linux命令按照順序依次寫入到一個文件中,這就是一個簡單的腳本了
[root@linuxprobe ~]# vim example.sh
#!/bin/bash
#For Example BY linuxprobe.com
pwd
ls -al
shell腳本文件的名稱可以任意,但為了避免被誤以為是普通文件,建議將.sh后綴加上,以表示是一個腳本文件。在上面的這個example.sh腳本中實際上出現了三種不同的元素:第一行的腳本聲明(#!)用來告訴系統使用哪種Shell解釋器來執行該腳本;第二行的注釋信息(#)是對腳本功能和某些命令的介紹信息,使得自己或他人在日后看到這個腳本內容時,可以快速知道該腳本的作用或一些警告信息;第三、四行的可執行語句也就是我們平時執行的Linux命令了。
[root@linuxprobe ~]# bash example.sh
/root/Desktop
total 8
drwxr-xr-x. 2 root root 23 Jul 23 17:31 .
dr-xr-x—. 14 root root 4096 Jul 23 17:31 …
-rwxr–r--. 1 root root 55 Jul 23 17:31 example.sh
用bash解釋器命令直接運行Shell腳本文件外,第二種運行腳本程序的方法是通過輸入
完整路徑的方式來執行。但默認會因為權限不足而提示報錯信息,此時只需要為腳本文件增加執行權限即可
[root@linuxprobe ~]# ./example.sh
bash: ./Example.sh: Permission denied
[root@linuxprobe ~]# chmod u+x example.sh
[root@linuxprobe ~]# ./example.sh
/root/Desktop
total 8
drwxr-xr-x. 2 root root 23 Jul 23 17:31 .
dr-xr-x—. 14 root root 4096 Jul 23 17:31 …
-rwxr–r--. 1 root root 55 Jul 23 17:31 example.sh
接收用戶的參數
像上面這樣的腳本程序只能執行一些預先定義好的功能,未免太過死板了。為了讓Shell腳本程序更好地滿足用戶的一些實時需求,以便靈活完成工作,必須要讓腳本程序能夠像之前執行命令時那樣,接收用戶輸入的參數。
Linux系統中的Shell腳本語言早就考慮到了這些,已經內設了用于接收參數的變量,變量之間可以使用空格間隔。例如0對應的是當前Shell腳本程序的名稱,0對應的是當前Shell腳本程序的名稱,0對應的是當前Shell腳本程序的名稱,#對應的是總共有幾個參數,?對應的是所有位置的參數值,*對應的是所有位置的參數值,?對應的是所有位置的參數值,?對應的是顯示上一次命令的執行返回值,而$1、$2、$3……則分別對應著第N個位置的參數值
嘗試編寫一個腳本程序示例,通過引用上面的變量參數來看下真實效果
[root@linuxprobe ~]# vim example.sh
#!/bin/bash
echo “當前腳本名稱為0"echo"總共有0" echo "總共有0"echo"總共有#個參數,分別是$*。”
echo “第1個參數為$1,第5個為$5。”
[root@linuxprobe ~]# sh example.sh one two three four five six
當前腳本名稱為example.sh
總共有6個參數,分別是one two three four five six。
第1個參數為one,第5個為five
在本書前面章節中講到,系統在執行mkdir命令時會判斷用戶輸入的信息,即判斷用戶指定的文件夾名稱是否已經存在,如果存在則提示報錯;反之則自動創建。Shell腳本中的條件測試語法可以判斷表達式是否成立,若條件成立則返回數字0,否則便返回其他隨機數值。條件測試語法的執行格式如圖4-16所示。切記,條件表達式兩邊均應有一個空格。
按照測試對象來劃分,條件測試語句可以分為4種:
文件測試語句;
邏輯測試語句;
整數值比較語句;
字符串比較語句。
文件測試即使用指定條件來判斷文件是否存在或權限是否滿足等情況的運算符,具體的參數如表4-3所示。
表4-3 文件測試所用的參數
運算符 作用
-d 測試文件是否為目錄類型
-e 測試文件是否存在
-f 判斷是否為一般文件
-r 測試當前用戶是否有權限讀取
-w 測試當前用戶是否有權限寫入
-x 測試當前用戶是否有權限執行
下面使用文件測試語句來判斷/etc/fstab是否為一個目錄類型的文件,然后通過Shell解釋器的內設$?變量顯示上一條命令執行后的返回值。如果返回值為0,則目錄存在;如果返回值為非零的值,則意味著目錄不存在
[root@linuxprobe ~]# [ -d /etc/fstab ]
[root@linuxprobe ~]# echo $?
1
再使用文件測試語句來判斷/etc/fstab是否為一般文件,如果返回值為0,則代表文件存在,且為一般文件
[root@linuxprobe ~]# [ -f /etc/fstab ]
[root@linuxprobe ~]# echo $?
0
輯語句用于對測試結果進行邏輯分析,根據測試結果可實現不同的效果。例如在Shell終端中邏輯“與”的運算符號是&&,它表示當前面的命令執行成功后才會執行它后面的命令,因此可以用來判斷/dev/cdrom文件是否存在,若存在則輸出Exist字樣
[root@linuxprobe ~]# [ -e /dev/cdrom ] && echo “Exist”
Exist
除了邏輯“與”外,還有邏輯“或”,它在Linux系統中的運算符號為||,表示當前面的命令執行失敗后才會執行它后面的命令,因此可以用來結合系統環境變量USER來判斷當前登錄的用戶是否為非管理員身份
[root@linuxprobe ~]# echo $USER
root
[root@linuxprobe ~]# [ KaTeX parse error: Expected 'EOF', got '#' at position 49: …t@linuxprobe ~]#? su - linuxprob… [ USER=root]∣∣echo"user"user第三種邏輯語句是“非”,在Linux系統中的運算符號是一個嘆號(!),它表示把條件測試中的判斷結果取相反值。也就是說,如果原本測試的結果是正確的,則將其變成錯誤的;原本測試錯誤的結果則將其變成正確的。我們現在切換回到root管理員身份,再判斷當前用戶是否為一個非管理員的用戶。由于判斷結果因為兩次否定而變成正確,因此會正常地輸出預設信息linuxprobe@linuxprobe]USER = root ] || echo "user" user 第三種邏輯語句是“非”,在Linux系統中的運算符號是一個嘆號(!),它表示把條件測試中的判斷結果取相反值。也就是說,如果原本測試的結果是正確的,則將其變成錯誤的;原本測試錯誤的結果則將其變成正確的。 我們現在切換回到root管理員身份,再判斷當前用戶是否為一個非管理員的用戶。由于判斷結果因為兩次否定而變成正確,因此會正常地輸出預設信息 linuxprobe@linuxprobe ~]USER=root]∣∣echo"user"user第三種邏輯語句是“非”,在Linux系統中的運算符號是一個嘆號(!),它表示把條件測試中的判斷結果取相反值。也就是說,如果原本測試的結果是正確的,則將其變成錯誤的;原本測試錯誤的結果則將其變成正確的。我們現在切換回到root管理員身份,再判斷當前用戶是否為一個非管理員的用戶。由于判斷結果因為兩次否定而變成正確,因此會正常地輸出預設信息linuxprobe@linuxprobe?] exit
logout
[root@linuxprobe root]# [ ! $USER = root ] || echo “administrator”
administrator
當前我們正在登錄的即為管理員用戶—root。下面這個示例的執行順序是,先判斷當前登錄用戶的USER變量名稱是否等于root,然后用邏輯運算符“非”進行取反操作,效果就變成了判斷當前登錄的用戶是否為非管理員用戶了。最后若條件成立則會根據邏輯“與”運算符輸出user字樣;或條件不滿足則會通過邏輯“或”運算符輸出root字樣,而如果前面的&&不成立才會執行后面的||符號
[root@linuxprobe ~]# [ ! $USER = root ] && echo “user” || echo “root”
root
整數比較運算符僅是對數字的操作,不能將數字與字符串、文件等內容一起操作,而且不能想當然地使用日常生活中的等號、大于號、小于號等來判斷。因為等號與賦值命令符沖突,大于號和小于號分別與輸出重定向命令符和輸入重定向命令符沖突。因此一定要使用規范的整數比較運算符來進行操作。可用的整數比較運算符如表4-4所示
可用的整數比較運算符
運算符 作用
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-lt 是否小于
-le 是否等于或小于
-ge 是否大于或等于
接下來小試牛刀。我們先測試一下10是否大于10以及10是否等于10(通過輸出的返回值內容來判斷
[root@linuxprobe ~]# [ 10 -gt 10 ]
[root@linuxprobe ~]# echo $?
1
[root@linuxprobe ~]# [ 10 -eq 10 ]
[root@linuxprobe ~]# echo $?
0
2.4節曾經講過free命令,它可以用來獲取當前系統正在使用及可用的內存量信息。接下來先使用free -m命令查看內存使用量情況(單位為MB),然后通過grep Mem:命令過濾出剩余內存量的行,再用awk '{print $4}'命令只保留第四列,最后用FreeMem=語句的方式把語句內執行的結果賦值給變量。
這個演示確實有些難度,但看懂后會覺得很有意思,沒準在運維工作中也會用得上
[root@linuxprobe ~]# free -m
total used free shared buffers cached
Mem: 1826 1244 582 9 1 413
-/+ buffers/cache: 830 996
Swap: 2047 0 2047
[root@linuxprobe ~]# free -m | grep Mem:
Mem: 1826 1244 582 9
[root@linuxprobe ~]# free -m | grep Mem: | awk ‘{print $4}’
582
[root@linuxprobe ~]# FreeMem=free -m | grep Mem: | awk '{print $4}'
[root@linuxprobe ~]# echo $FreeMem
用于獲取內存可用量的命令以及步驟可能有些“超綱”了,如果不能理解領會也不用擔心,接下來才是重點。我們使用整數運算符來判斷內存可用量的值是否小于1024,若小于則會提示“Insufficient Memory”(內存不足)的字樣
[root@linuxprobe ~]# [ $FreeMem -lt 1024 ] && echo “Insufficient Memory”
Insufficient Memor
[root@linuxprobe ~]# [ $FreeMem -lt 1024 ] && echo “Insufficient Memory”
Insufficient Memory
字符串比較語句用于判斷測試字符串是否為空值,或兩個字符串是否相同。它經常用來判斷某個變量是否未被定義(即內容為空值),理解起來也比較簡單。字符串比較中常見的運算符如表4-5所示。
表4-5 常見的字符串比較運算符
運算符 作用
= 比較字符串內容是否相同
!= 比較字符串內容是否不同
-z 判斷字符串內容是否為空
接下來通過判斷String變量是否為空值,進而判斷是否定義了這個變量
[root@linuxprobe ~]# [ -z $String]
[root@linuxprobe ~]# echo $?
0
再嘗試引入邏輯運算符來試一下。當用于保存當前語系的環境變量值LANG不是英語(en.US)時,則會滿足邏輯測試條件并輸出“Not en.US”(非英語)的字樣
[root@linuxprobe ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 37: …t@linuxprobe ~]#? [LANG != “en.US”] && echo “Not en.US”
Not en.US
此時可以通過使用Linux命令、管道符、重定向以及條件測試語句來編寫最基本的Shell腳本,但是這種腳本并不適用于生產環境。原因是它不能根據真實的工作需求來調整具體的執行命令,也不能根據某些條件實現自動循環執行。例如,我們需要批量創建1000位用戶,首先要判斷這些用戶是否已經存在;若不存在,則通過循環語句讓腳本自動且依次創建他們。
接下來我們通過if、for、while、case這4種流程控制語句來學習編寫難度更大、功能更強的Shell腳本。為了保證下文的實用性和趣味性,做到寓教于樂,我會盡可能多地講解各種不同功能的Shell腳本示例,而不是逮住一個腳本不放,在它原有內容的基礎上修修補補。盡管這種修補式的示例教學也可以讓讀者明白理論知識,但是卻無法開放思路,不利于日后的工作。
if條件測試語句
if條件測試語句可以讓腳本根據實際情況自動執行相應的命令。從技術角度來講,if語句分為單分支結構、雙分支結構、多分支結構;其復雜度隨著靈活度一起逐級上升。
if條件語句的單分支結構由if、then、fi關鍵詞組成,而且只在條件成立后才執行預設的命令,相當于口語的“如果……那么……”。單分支的if語句屬于最簡單的一種條件判斷結構,語法格式如圖4-17所示。
下面使用單分支的if條件語句來判斷/media/cdrom文件是否存在,若存在就結束條件判斷和整個Shell腳本,反之則去創建這個目錄
[root@linuxprobe ~]# vim mkcdrom.sh
#!/bin/bash
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
于第5章才講解用戶身份與權限,因此這里繼續用“bash 腳本名稱”的方式來執行腳本。在正常情況下,順利執行完腳本文件后沒有任何輸出信息,但是可以使用ls命令驗證/media/cdrom目錄是否已經成功創建
[root@linuxprobe ~]# bash mkcdrom.sh
[root@linuxprobe ~]# ls -d /media/cdrom
/media/cdrom
f條件語句的雙分支結構由if、then、else、fi關鍵詞組成,它進行一次條件匹配判斷,如果與條件匹配,則去執行相應的預設命令;反之則去執行不匹配時的預設命令,相當于口語的“如果……那么……或者……那么……”。if條件語句的雙分支結構也是一種很簡單的判斷結構,語法格式如圖4-18所示。
下面使用雙分支的if條件語句來驗證某臺主機是否在線,然后根據返回值的結果,要么顯示主機在線信息,要么顯示主機不在線信息。這里的腳本主要使用ping命令來測試與對方主機的網絡聯通性,而Linux系統中的ping命令不像Windows一樣嘗試4次就結束,因此為了避免用戶等待時間過長,需要通過-c參數來規定嘗試的次數,并使用-i參數定義每個數據包的發送間隔,以及使用-W參數定義等待超時時間
[root@linuxprobe ~]# vim chkhost.sh
#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo “Host $1 is On-line.”
else
echo “Host 1isOff?line."fi我們在4.2.3小節中用過1 is Off-line." fi 我們在4.2.3小節中用過1isOff?line."fi我們在4.2.3小節中用過?變量,作用是顯示上一次命令的執行返回值。若前面的那條語句成功執行,則?變量會顯示數字0,反之則顯示一個非零的數字(可能為1,也可能為2,取決于系統版本)。因此可以使用整數比較運算符來判斷?變量會顯示數字0,反之則顯示一個非零的數字(可能為1,也可能為2,取決于系統版本)。因此可以使用整數比較運算符來判斷?變量會顯示數字0,反之則顯示一個非零的數字(可能為1,也可能為2,取決于系統版本)。因此可以使用整數比較運算符來判斷?變量是否為0,從而獲知那條語句的最終判斷情況。這里的服務器IP地址為192.168.10.10,我們來驗證一下腳本的效果
[root@linuxprobe ~]# bash chkhost.sh 192.168.10.10
Host 192.168.10.10 is On-line.
[root@linuxprobe ~]# bash chkhost.sh 192.168.10.20
Host 192.168.10.20 is Off-line
if條件語句的多分支結構由if、then、else、elif、fi關鍵詞組成,它進行多次條件匹配判斷,這多次判斷中的任何一項在匹配成功后都會執行相應的預設命令,相當于口語的“如果……那么……如果……那么……”。if條件語句的多分支結構是工作中最常使用的一種條件判斷結構,盡管相對復雜但是更加靈活,語法格式如圖4-19所示。
下面使用多分支的if條件語句來判斷用戶輸入的分數在哪個成績區間內,然后輸出如Excellent、Pass、Fail等提示信息。在Linux系統中,read是用來讀取用戶輸入信息的命令,能夠把接收到的用戶輸入信息賦值給后面的指定變量,-p參數用于向用戶顯示一定的提示信息。在下面的腳本示例中,只有當用戶輸入的分數大于等于85分且小于等于100分,才輸出Excellent字樣;若分數不滿足該條件(即匹配不成功),則繼續判斷分數是否大于等于70分且小于等于84分,如果是,則輸出Pass字樣;若兩次都落空(即兩次的匹配操作都失敗了),則輸出Fail字樣
[root@linuxprobe ~]# vim chkscore.sh
#!/bin/bash
read -p “Enter your score(0-100):” GRADE
if [ $GRADE -ge 85 ] && [ GRADE?le100];thenecho"GRADE -le 100 ] ; then echo "GRADE?le100];thenecho"GRADE is Excellent”
elif [ $GRADE -ge 70 ] && [ GRADE?le84];thenecho"GRADE -le 84 ] ; then echo "GRADE?le84];thenecho"GRADE is Pass"
else
echo “$GRADE is Fail”
fi
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):88
88 is Excellent
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):80
80 is Pass
下面執行該腳本。當用戶輸入的分數分別為30和200時,其結果如下
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):30
30 is Fail
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):200
200 is Fail
為什么輸入的分數為200時,依然顯示Fail呢?原因很簡單—沒有成功匹配腳本中的兩個條件判斷語句,因此自動執行了最終的兜底策略。可見,這個腳本還不是很完美,建議讀者自行完善這個腳本,使得用戶在輸入大于100或小于0的分數時,給予Error報錯字樣的提示
for條件循環語句
for循環語句允許腳本一次性讀取多個信息,然后逐一對信息進行操作處理,當要處理的數據有范圍時,使用for循環語句再適合不過了。for循環語句的語法格式如圖4-20所示
下面使用for循環語句從列表文件中讀取多個用戶名,然后為其逐一創建用戶賬戶并設置密碼。首先創建用戶名稱的列表文件users.txt,每個用戶名稱單獨一行。讀者可以自行決定具體的用戶名稱和個數
[root@linuxprobe ~]# vim users.txt
andy
barry
carl
duke
eric
george
下來編寫Shell腳本Example.sh。在腳本中使用read命令讀取用戶輸入的密碼值,然后賦值給PASSWD變量,并通過-p參數向用戶顯示一段提示信息,告訴用戶正在輸入的內容即將作為賬戶密碼。在執行該腳本后,會自動使用從列表文件users.txt中獲取到所有的用戶名稱,然后逐一使用“id 用戶名”命令查看用戶的信息,并使用$?判斷這條命令是否執行成功,也就是判斷該用戶是否已經存在。
需要多說一句,/dev/null是一個被稱作Linux黑洞的文件,把輸出信息重定向到這個文件等同于刪除數據(類似于沒有回收功能的垃圾箱),可以讓用戶的屏幕窗口保持簡潔
[root@linuxprobe ~]# vim Example.sh
#!/bin/bash
read -p “Enter The Users Password : " PASSWD
for UNAME in cat users.txt
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo “Already exists”
else
useradd KaTeX parse error: Expected 'EOF', got '&' at position 7: UNAME &?> /dev/null ech…PASSWD” | passwd --stdin $UNAME &> /dev/null
if [ ??eq0]thenecho"? -eq 0 ] then echo "??eq0]thenecho"UNAME , Create success"
else
echo “KaTeX parse error: Expected 'EOF', got '#' at position 151: …t@linuxprobe ~]#? vim users.txt …?判斷這條命令是否執行成功,也就是判斷該用戶是否已經存在。
需要多說一句,/dev/null是一個被稱作Linux黑洞的文件,把輸出信息重定向到這個文件等同于刪除數據(類似于沒有回收功能的垃圾箱),可以讓用戶的屏幕窗口保持簡潔
[root@linuxprobe ~]# vim Example.sh
#!/bin/bash
read -p “Enter The Users Password : " PASSWD
for UNAME in cat users.txt
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo “Already exists”
else
useradd KaTeX parse error: Expected 'EOF', got '&' at position 7: UNAME &?> /dev/null ech…PASSWD” | passwd --stdin $UNAME &> /dev/null
if [ ??eq0]thenecho"? -eq 0 ] then echo "??eq0]thenecho"UNAME , Create success”
else
echo "KaTeX parse error: Expected 'EOF', got '#' at position 283: …t@linuxprobe ~]#? bash Example.s…(命令)是一種完全類似于第3章的轉義字符中反引號命令的Shell操作符,效果同樣是執行括號或雙引號括起來的字符串中的命令。大家在編寫腳本時,多學習幾種類似的新方法,可在工作中大顯身手
[root@linuxprobe ~]# vim CheckHosts.sh
#!/bin/bash
HLIST=$(cat ~/ipadds.txt)
for IP in $HLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ] ; then
echo “Host $IP is On-line.”
else
echo “Host KaTeX parse error: Expected 'EOF', got '#' at position 45: …t@linuxprobe ~]#? ./CheckHosts.s…(expr $RANDOM % 1000)
TIMES=0
echo “商品實際價格為0-999之間,猜猜看是多少?”
while true
do
read -p “請輸入您猜測的價格數目:” INT
let TIMES++
if [ $INT -eq $PRICE ] ; then
echo “恭喜您答對了,實際價格是 $PRICE”
echo “您總共猜g $TIMES 次”
exit 0
elif [ $INT -gt KaTeX parse error: Expected 'EOF', got '#' at position 196: …t@linuxprobe ~]#? bash Guess.sh …KEY” in
[a-z]|[A-Z])
echo “您輸入的是 字母。”
;;
[0-9])
echo “您輸入的是 數字。”
;;
*)
echo “您輸入的是 空格、功能鍵或其他控制字符。”
esac
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符,并按Enter鍵確認:6
您輸入的是 數字。
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符,并按Enter鍵確認:p
您輸入的是 字母。
[root@linuxprobe ~]# bash Checkkeys.sh
請輸入一個字符,并按Enter鍵確認:^[[15~
您輸入的是 空格、功能鍵或其他控制字符
計劃任務服務程
計劃任務分為一次性計劃任務與長期性計劃任務,大家可以按照如下方式理解。
一次性計劃任務:今晚11點30分開啟網站服務。
長期性計劃任務:每周一的凌晨3點25分把/home/wwwroot目錄打包備份為backup.tar.gz。
顧名思義,一次性計劃任務只執行一次,一般用于滿足臨時的工作需求。我們可以用at命令實現這種功能,只需要寫成“at 時間”的形式就可以。如果想要查看已設置好但還未執行的一次性計劃任務,可以使用“at -l”命令;要想將其刪除,可以用“atrm 任務序號”。在使用at命令來設置一次性計劃任務時,默認采用的是交互式方法。例如,使用下述命令將系統設置為在今晚23:30分自動重啟網站服務
[root@linuxprobe ~]# at 23:30
at > systemctl restart httpd
at > 此處請同時按下Ctrl + D組合鍵來結束編寫計劃任務
job 3 at Mon Apr 27 23:30:00 2017
[root@linuxprobe ~]# at -l
3 Mon Apr 27 23:30:00 2017 a root
讀者想挑戰一下難度更大但簡捷性更高的方式,可以把前面學習的管道符(任意門)放到兩條命令之間,讓at命令接收前面echo命令的輸出信息,以達到通過非交互式的方式創建計劃一次性任務的目的
[root@linuxprobe ~]# echo “systemctl restart httpd” | at 23:30
job 4 at Mon Apr 27 23:30:00 2017
[root@linuxprobe ~]# at -l
3 Mon Apr 27 23:30:00 2017 a root
4 Mon Apr 27 23:30:00 2017 a root
我們不小心設置了兩個一次性計劃任務,可以使用下面的命令輕松刪除其中一個
[root@linuxprobe ~]# atrm 3
[root@linuxprobe ~]# at -l
4 Mon Apr 27 23:30:00 2017 a root
我們希望Linux系統能夠周期性地、有規律地執行某些具體的任務,那么Linux系統中默認啟用的crond服務簡直再適合不過了。創建、編輯計劃任務的命令為“crontab -e”,查看當前計劃任務的命令為“crontab -l”,刪除某條計劃任務的命令為“crontab -r”。另外,如果您是以管理員的身份登錄的系統,還可以在crontab命令中加上-u參數來編輯他人的計劃任務。
在正式部署計劃任務前,請先跟劉遄老師念一下口訣“分、時、日、月、星期 命令”。這是使用crond服務設置任務的參數格式(其格式見表4-6)。需要注意的是,如果有些字段沒有設置,則需要使用星號()占位,如圖4-23所示。
使用crond設置任務的參數字段說明
字段 說明
分 取值為0~59的整數
時 取值為0~23的任意整數
日 取值為1~31的任意整數
月 取值為1~12的任意整數
星期 取值為0~7的任意整數,其中0與7均為星期日
命令 要執行的命令或程序腳本
假設在每周一、三、五的凌晨3點25分,都需要使用tar命令把某個網站的數據目錄進行打包處理,使其作為一個備份文件。我們可以使用crontab -e命令來創建計劃任務。為自己創建計劃任務無需使用-u參數,具體的實現效果的參數如crontab -l命令結果所示
[root@linuxprobe ~]# crontab -e
no crontab for root - using an empty one
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
說明的是,除了用逗號(,)來分別表示多個時間段,例如“8,9,12”表示8月、9月和12月。還可以用減號(-)來表示一段連續的時間周期(例如字段“日”的取值為“12-15”,則表示每月的12~15日)。以及用除號(/)表示執行任務的間隔時間(例如“/2”表示每隔2分鐘執行一次任務)。
如果在crond服務中需要同時包含多條計劃任務的命令語句,應每行僅寫一條。例如我們再添加一條計劃任務,它的功能是每周一至周五的凌晨1點鐘自動清空/tmp目錄內的所有文件。尤其需要注意的是,在crond服務的計劃任務參數中,所有命令一定要用絕對路徑的方式來寫,如果不知道絕對路徑,請用whereis命令進行查詢,rm命令路徑為下面輸出信息中加粗部分
[root@linuxprobe ~]# whereis rm
rm: /usr/bin/rm /usr/share/man/man1/rm.1.gz /usr/share/man/man1p/rm.1p.gz
[root@linuxprobe ~]# crontab -e
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
0 1 * * 1-5 /usr/bin/rm -rf /tmp/*
在本節最后,劉遄老師再來啰嗦幾句在工作中使用計劃服務的注意事項。
在crond服務的配置參數中,可以像Shell腳本那樣以#號開頭寫上注釋信息,這樣在日后回顧這段命令代碼時可以快速了解其功能、需求以及編寫人員等重要信息。
計劃任務中的“分”字段必須有數值,絕對不能為空或是*號,而“日”和“星期”字段不能同時使用,否則就會發生沖突。
最后再啰嗦一句,想必讀者也已經發現了,諸如crond在內的很多服務默認調用的是Vim編輯器,相信大家現在能進一步體會到在Linux系統中掌握Vim文本編輯器的好處了吧。所以請大家一定要在徹底掌握Vim編碼器之后再學習下一章
總結
- 上一篇: 22.仿传智播客导航栏案例(精灵图案例)
- 下一篇: 手把手教你如何剪短视频时长