第 8 堂課:bash 指令連續下達與資料流重導向
http://linux.vbird.org/linux_basic_train/unit08.php
8.1:連續指令的下達
8.1.1:指令回傳值
例題:請將『變數』、『指令』、『數學計算式』、『純文字』、『保持 $ 功能』等寫入下方的空格中
- var=${ }
- var=$( )
- var=$(( ))
- var=" "
- var=' '
- var=` `
看不太懂這題要做啥
例題:請找出 ifconfig 與 chfn 等指令後,列出該指令的權限
- 先以 which command 的方式找出指令的全名
- 將上述的結果,使用 ls -l 顯示出來。請搭配前一個例題的符號 ($, ', ", ` 等) 來處理。
#1
[root@mail ~]# which ifconfig
/usr/sbin/ifconfig
[root@mail ~]# which chfn
/usr/bin/chfn
#2
看不太懂這提要做啥
例題:了解各指令回傳值的意義
- 輸入『 /etc/passwd 』這個檔名在指令列,之後再輸入『 echo $? 』觀察輸出的號碼為何?
- 輸入『 vbirdcommand 』這個指令名,之後再輸入『 echo $? 』觀察輸出的號碼為何?
- 因為尚未進入其他指令 (上述兩個檔名、指令都是不正確的),因此這些錯誤訊息應該來自於 bash 本身的判斷。 因此使用『 man bash 』後,查詢『 ^exit status 』關鍵字,找出上述號碼的意義為何?
- 承上,該段文字敘述中,解釋的回傳值共有幾個號碼?
- 輸入『 ls /vbird 』,之後觀察輸出的回傳值,查詢該回傳值的意義為何?(你應該要 man ls 還是 man bash 呢?)
#1
[root@mail ~]# /etc/passwd
-bash: /etc/passwd: 拒絕不符權限的操作
[root@mail ~]# echo $?
126
#2
[root@mail ~]# vbirdcommand
-bash: vbirdcommand:命令找不到
[root@mail ~]# echo $?
127
#3
EXIT STATUS
The exit status of an executed command is the value returned by the waitpid system call or equivalent function. Exit statuses fall between 0 and 255, though, as
explained below, the shell may use values above 125 specially. Exit statuses from shell builtins and compound commands are also limited to this range. Under certain
circumstances, the shell will use special values to indicate specific failure modes.
For the shell's purposes, a command which exits with a zero exit status has succeeded. An exit status of zero indicates success. A non-zero exit status indicates
failure. When a command terminates on a fatal signal N, bash uses the value of 128+N as the exit status.
If a command is not found, the child process created to execute it returns a status of 127. If a command is found but is not executable, the return status is 126.
If a command fails because of an error during expansion or redirection, the exit status is greater than zero.
Shell builtin commands return a status of 0 (true) if successful, and non-zero (false) if an error occurs while they execute. All builtins return an exit status of 2
to indicate incorrect usage.
Bash itself returns the exit status of the last command executed, unless a syntax error occurs, in which case it exits with a non-zero value. See also the exit
builtin command below.
#4
0跟126跟127
#5
[root@mail ~]# ls /vbird
ls: cannot access /vbird: No such file or directory
[root@mail ~]# echo $?
2
#要man bash
8.1.2:連續指令的下達
上述的指令能否寫成:『 ls -d /vbird &> /dev/null || echo non-exist && echo exist 』,嘗試說明原因。
不行
執行 "ls -d /vbird &> /dev/null" status結果會是2
執行 "ls -d /vbird &> /dev/null || echo non-exist" status 結果會是0
所以執行"ls -d /vbird &> /dev/null || echo non-exist && echo exist"會顯示以下內容
-----------------------------------
non-exist
exist
8.1.3:使用 test 及 [ 判斷式 ] 確認回傳值
使用 test 判斷 /etc/ 是否存在,然後顯示回傳值
[root@mail ~]# test -e /etc/ ; echo $?
0
使用 test 判斷 /usr/bin/passwd 是否具有 SUID ,然後顯示回傳值
[root@mail ~]# test -u /usr/bin/passwd ; echo $?
0
使用 test 判斷 ${HOSTNAME} 是否等於 "mylocalhost" ,然後顯示回傳值
[root@mail ~]# test ${HOSTNAME} == mylocalhost ; echo $?
1
修改 /usr/local/bin/checkfile ,取消 ls 的判斷,改用 test 判斷
//TODO
使用 [ ] 判斷 /etc/ 是否存在,然後顯示回傳值
[root@mail ~]# [ -e /etc/ ] ; echo $?
0
使用 [ ] 判斷 /usr/bin/passwd 是否具有 SUID ,然後顯示回傳值
[root@mail ~]# [ -u /usr/bin/passwd ] ; echo $?
0
使用 [ ] 判斷 ${HOSTNAME} 是否等於 "mylocalhost" ,然後顯示回傳值
[root@mail ~]# [ ${HOSTNAME} == mylocalhost ] ; echo $?
1
8.1.4:命令別名
使用 root 的身份切換路徑到 /dev/shm 底下
[root@mail ~]# cd /dev/shm
將 /etc 整個目錄複製到 『本目錄』 底下
[root@mail shm]# cp -r /etc .
若將上述指令重新執行一次,會發生什麼問題?
[root@mail shm]# cp -r /etc .
cp: overwrite './etc/fstab'? y
若你確定檔名就是需要覆蓋,那可以使用什麼方式來處理 (思考 A. 使用絕對路徑不要用命令別名 B. 讓指令自動忽略命令別名)
#cp 已經被alias設定成 cp -i
#A
[root@mail shm]# /bin/cp -rf /etc .
#B
yes | cp -r /etc .
由於我們的 student 帳號也算管理員常用的帳號,因此建議也將 cp, mv, rm 預設加上 -i 的選項。 請問如何處理?
[root@mail shm]# su -student
su: failed to execute tudent: No such file or directory
[root@mail shm]# su - student
Last login: Sun May 8 20:07:18 CST 2016 on pts/1
[student@mail ~ 23:02 #1]$ vi .bashrc
#在student家目錄下`:q的.bashrc中,加上
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
8.1.5:用 () 進行資料彙整
設定一個命令別名為 geterr ,內容為執行 echo "I am error" 1>&2
[student@mail ~ 23:30 #1]$ vi .bashrc
#在.bashrc加上
geterr='echo "I am error" 1>&2'
執行 geterr 得到什麼結果?
[student@mail ~ 23:37 #1]$ geterr
I am error
執行 geterr 2> /dev/null 得到什麼結果?
[student@mail ~ 23:38 #3]$ geterr 2> /dev/null
I am error
執行 (geterr) 2> /dev/null 得到什麼結果?
[student@mail ~ 23:38 #4]$ (geterr) 2> /dev/null
嘗試解析為何會這樣?
(geterr) 將所有訊息丟到垃圾桶吧?
8.2:資料流重導向
8.2.1:指令執行資料的流動
以 student 身份執行找檔名的任務,指令為『 find /etc -name '*passwd*' 』,螢幕輸出什麼資訊?請了解 * 的意義。
find: ‘/etc/grub.d’: 拒絕不符權限的操作
find: ‘/etc/selinux/targeted/active’: 拒絕不符權限的操作
find: ‘/etc/selinux/final’: 拒絕不符權限的操作
/etc/passwd
/etc/passwd-
find: ‘/etc/pki/CA/private’: 拒絕不符權限的操作
find: ‘/etc/pki/rsyslog’: 拒絕不符權限的操作
find: ‘/etc/polkit-1/rules.d’: 拒絕不符權限的操作
find: ‘/etc/polkit-1/localauthority’: 拒絕不符權限的操作
find: ‘/etc/dhcp’: 拒絕不符權限的操作
find: ‘/etc/lvm/archive’: 拒絕不符權限的操作
find: ‘/etc/lvm/backup’: 拒絕不符權限的操作
find: ‘/etc/lvm/cache’: 拒絕不符權限的操作
/etc/security/opasswd
/etc/pam.d/passwd
find: ‘/etc/vmware-tools/GuestProxyData/trusted’: 拒絕不符權限的操作
find: ‘/etc/firewalld’: 拒絕不符權限的操作
find: ‘/etc/audisp’: 拒絕不符權限的操作
find: ‘/etc/audit’: 拒絕不符權限的操作
find: ‘/etc/sudoers.d’: 拒絕不符權限的操作
/etc/dovecot/conf.d/auth-passwdfile.conf.ext
student 對於系統設定檔原本就有很多的無權限目錄,因此請將錯誤訊息直接丟棄 (導向於 /dev/null )
[student@mail ~ 23:48 #16]$ find /etc -name '*passwd*' 2> /dev/null
/etc/passwd
/etc/passwd-
/etc/security/opasswd
/etc/pam.d/passwd
/etc/dovecot/conf.d/auth-passwdfile.conf.ext
將最終螢幕的輸出轉存成為 ~student/find_passwd.txt 檔案
[student@mail ~ 23:49 #17]$ find /etc -name '*passwd*' 2> /dev/null > ~student/find_passwd.txt
再以 student 的身份重新搜尋 /etc 底下檔名為 *shadow* 的資料,將正確訊息『累加』到 ~student/find_passwd.txt 檔案內
[student@mail ~ 23:49 #18]$ find /etc -name *shadow* 2> /dev/null >> ~student/find_passwd.txt
查看 ~student/find_passwd.txt 檔案內是否同時具有 passwd 與 shadow 相關的檔名存在?
[student@mail ~ 23:50 #19]$ cat find_passwd.txt
/etc/passwd
/etc/passwd-
/etc/security/opasswd
/etc/pam.d/passwd
/etc/dovecot/conf.d/auth-passwdfile.conf.ext
/etc/gshadow
/etc/gshadow-
/etc/shadow-
/etc/shadow
直接用 student 身份執行『 cat 』這個指令,然後隨意輸入兩串文字,查閱指令執行的結果為何? 要結束指令的輸入請執行 [ctrl]+d 結束 (並不是 [ctrl]+c 喔!)
[student@mail ~ 23:56 #5]$ cat
123456123456[student@mail ~ 23:56 #6]$
#可以打字在螢幕上,打123456後,第一次按[ctrl] + d複製貼上了123456,第二次才回到可以輸入命令的狀態
若輸入的字串可以直接轉存成為 mycat.txt 檔案,該如何下達指令?
[student@mail ~ 23:56 #6]$ cat >> mycat.txt
12344556
[student@mail ~ 00:00 #7]$ cat mycat.txt
12344556
將 /etc/hosts 透過 cat 讀入 (使用兩種方式,直接讀入與透過 < 方式讀入)
#1
[student@mail ~ 00:00 #8]$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.231.8.147 mail.want-want.com
#2
[student@mail ~ 00:02 #9]$ cat < /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.231.8.147 mail.want-want.com
將 /etc/hosts 透過 cat 以 < 的方式讀入後,累加輸出到 mycat.txt 檔案內
[student@mail ~ 00:02 #10]$ cat < /etc/hosts >> mycat.txt
[student@mail ~ 00:03 #11]$ cat mycat.txt
12344556
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.231.8.147 mail.want-want.com
8.2.2:管線 (pipe) | 的意義
例題:我想要查看 /etc 底下共有多少檔案檔名結尾為 .conf 的檔案個數,該如何處理?
- 找出檔名最好使用 find 來處理,因此請使用 find /etc 來查看一下資料
- 你可以透過『 find /etc -name '*.conf' 』或『 find /etc | grep '.conf' 』來找到所需要的檔名。由於 grep 可以加上顏色顯示,故建議可以嘗試使用 grep 的方式來顯示較佳。
- 最終計算檔案數,就可以使用 wc 這個指令來處理『 find /etc | grep '.conf' | wc -l 』
#1
[student@mail ~ 00:03 #12]$ find /etc -name '*.conf'
#2
[student@mail ~ 00:03 #12]$ find /etc | grep '\.conf'
#3
[student@mail ~ 00:03 #12]$ find /etc | grep '\.conf' | wc -l
例題:
- 在 /etc/passwd 裡面的第一欄位為帳號名稱,第七欄位為 shell ,我想要只找這兩個項目來輸出,該如何處理?
- 承上,若我只想要知道有多少個 shell (第七欄位),同時每個 shell 各有幾個,又可以如何處理?
- 使用 last 可以查看到每個用戶的登入情況,請透過 cut 取出第一個欄位後,分析每個帳號的登入次數為何
- 使用 ip addr show 可以查詢到每個網路界面的 IP 位址,若只想要取出 IPv4 的位址,應該如何處理?
- 承上,若只想要列出 IP 位址 (127.0.0.1/8 之類的),是否可以透過 awk 來達成?
- 承上,將取得的 IP 位址除了顯示在螢幕上,亦同步輸出到 /dev/shm/myip.txt 檔案中
#1
[student@mail ~ 00:07 #16]$ awk -F":" '{print $1 $7}' /etc/passwd
#2
[student@mail ~ 00:09 #17]$ awk -F":" '{print $7}' /etc/passwd | uniq -c
#3
[student@mail ~ 00:09 #18]$ last | cut -d" " -f 1 | uniq
#4
[student@mail ~ 00:09 #19]$ ip addr show wlan0 | grep -Po 'inet \K[\d.]+'
Device "wlan0" does not exist.
#5
[student@mail ~ 00:10 #20]$ ip addr show | grep "inet\b" | awk '{print $2}'
127.0.0.1/8
10.231.8.147/24
#6
[student@mail ~ 00:10 #21]$ (ip addr show | grep "inet\b" | awk '{print $2}') >/dev/shm/myip.txt
8.3:課後練習操作
嘗試以系統上面的實做資料後,回答下列問題,並將答案寫入 /root/ans08.txt 當中:
- 當執行完成 mysha.sh 這個指令之後,該指令的回傳值為多少?
- 在不透過 bc 這個指令的情況下,如何以 bash 的功能,計算一年有幾秒?亦即如何計算出 60*60*24*365 的結果?
- 使用 find / 找出全系統的檔名,然後將所有資料 (包括正確與錯誤) 全部寫入 /root/find_filename.txt , 請寫下達成此目標的完整指令方法。
- 寫下一段指令 (主要以 echo 來達成的),執行該段指令會輸出『 My $HOSTNAME is 'XXX' 』,其中 XXX 為使用 hostname 這個指令所輸出的主機名稱。例如主機名稱為 station1 時,該串指令會輸出『 My $HOSTNAME is 'station1'』。 該串指令在任何主機均可執行,但都會輸出不同的訊息(因為主機名稱不一樣所致)
- 管理員 (root) 執行 mv 時,由於預設 alias 的關係,都會主動的加上 mv -i 這個選項。請寫下兩個方法, 讓 root 執行 mv 時,不會有 -i 的預設選項 (不能 unalias 的情況下)
- 透過『 ll /usr/sbin/* /usr/bin/* 』搭配 cut 與 sort, uniq 等指令來設計一個指令串,執行該指令串之後會輸出如下的畫面, 請寫下該指令串:(底下畫面為示意圖,實際輸出的個數可能會有些許差異)
1 r-s--x--- 1 rw-r--r-- 10 rwsr-xr-x 3 rws--x--x 3 rwx------ 4 rwxr-sr-x 241 rwxrwxrwx 8 rwxr-x--- 1633 rwxr-xr-x... |
---|
#1
假設有,可以sh mysha.sh然後echo $?
#2
[root@mail ~]# echo "$((60 * 60 * 24 * 365))"
31536000
#3
[root@mail ~]# find / > /root/find_filename.txt
#4
[root@mail ~]# echo "My \$HOSTNAME is $HOSTNAME"
My $HOSTNAME is mail.want-want.com
#5
/usr/bin/mv
#6
[root@mail ~]# ll /usr/sbin/* /usr/bin/* | cut -d " " -f 1 | uniq -c
製作一個名為 mycmdperm.sh 的腳本指令,放置於 /usr/local/bin 裡面。該腳本的重點是這樣的:
- 執行腳本的方式為『 mycmdperm.sh command 』,其中 command 為你想要取得的指令的名稱
- 在 mycmdperm.sh 裡面,指定一個變數為 cmd ,這個變數的內容為 ${1},其中 ${1} 就是該腳本後面攜帶的第一個參數
- 使用『 ll $( which ${cmd} ) 』來取得這個 cmd 的實際權限。
- 讓 mycmdperm.sh 具有可執行權。
- 最終請執行一次該指令,例如使用『 mycmdperm.sh passwd 』應該會秀出 passwd 的相關權限。不過該指令應該會執行失敗, 因為上述的 (c) 指令怪怪的,似乎是『命令別名無法用在腳本內』的樣子。因此,請將這個腳本的內容修訂成為沒有問題的形式。 (就是將命令別名改成實際的指令操作)
#1
[root@mail ~]# vim /usr/local/bin/mycmdperm.sh
#2, 3
-----------------------------------------
#!/bin/sh
cmd=${1}
ls -l $(which ${cmd})
[root@mail ~]#
-----------------------------------------
#4
[root@mail ~]# chmod 777 /usr/local/bin/mycmdperm.sh
#5
[root@mail ~]# mycmdperm.sh passwd
-rwsr-xr-x. 1 root root 27832 Jun 10 2014 /usr/bin/passwd
製作一個名為 myfileperm.sh 的腳本指令,放置於 /usr/local/bin 裡面。該腳本的重點是這樣的:
- 執行腳本的方式為『 myfileperm.sh filename 』,其中 filename 為你想要取得的檔案名稱 (絕對路徑或相對路徑)
- 在 myfileperm.sh 裡面,指定一個變數為 filename ,這個變數的內容為 ${1},其中 ${1} 就是該腳本後面攜帶的第一個參數
- 判斷 filename 是否不存在,若不存在則回報『filename is non exist』
- 判斷 filename 存在,且為一般檔案,若是則回報『 filename is a reguler file 』
- 判斷 filename 存在,且為一般目錄,若是則回應『 filename is a directory』
#1
vim /usr/local/bin/myfileperm.sh
#2, 3, 4, 5
#!/bin/bash
[ -e ${1} ] || echo 'filename is non exist'
[ -f ${1} ] && echo 'filename is a requler file'
[ -d ${1} ] && echo 'filename is a directory'
#執行時要先 chmod 777 /usr/local/bin/myfileperm.sh
製作一個 mymsg.sh 的腳本指令,放置於 /usr/local/bin 底下:
- 主要使用 cat 搭配 << eof 這樣的指令語法來處理
- 當執行 mymsg.sh 時,螢幕會輸出底下的字樣,然後結束指令。
[student@localhost ~]$ mymsg.sh
Hollo!! My name is 'Internet Lover'...
My server's kernel version is $kver
I'm a student bye bye!!
[root@mail ~]# cat << eof > /usr/local/bin/mymsg.sh
echo -e "Hollo!! My name is 'Internet Lover'... \nMy server's kernel version is $kver \nI'm a student bye bye!!"
> -bash: warning: here-document at line 11 delimited by end-of-file (wanted `eof')
[root@mail ~]# sh /usr/local/bin/mymsg.sh
Hollo!! My name is 'Internet Lover'...
My server's kernel version is
I'm a student bye bye!!
檔案與檔案內容處理方法:
- 找出 /etc/services 這個檔案內含有 http 的關鍵字那幾行,並將該資料轉存成 /root/myhttpd.txt 檔案
- 找出 examuser 這個帳號在系統所擁有的檔案,並將這些檔案放置到 /root/examuser 目錄中
#1
[root@mail ~]# cat /etc/services | grep http > /root/myhttpd.txt
#2
[root@mail ~]# cp -a $(find / -user examuser -type f) /root/examuser
find: '/proc/21104/task/21104/fdinfo/6': No such file or directory
find: '/proc/21104/fdinfo/5': No such file or directory
root 的 bash 環境操作設定: (主要是修改 .bashrc 喔!且指令內的指令需要用到 $ 時,得輸入 $ 才可以)
- 做一個命令別名為 myip 的指令,這個指令會透過 ifconfig 的功能,顯示出 eth0 這張網卡的 IP (只要 IP 就好喔!)。例如 IP 為 192.168.251.12 時,則輸入 myip 這個指令,螢幕只會輸出 192.168.251.12 的意思。
- 建立一個命令別名 myerr 這個指令,這個指令會將『 echo "I am error message" 』這個訊息傳輸到 standard error output 去! 亦即當執行『 (myerr) 』時,會在螢幕上出現 I am error message,但是執行『 (myerr) 2> /dev/null 』時,螢幕不會有任何訊息的輸出。
#1
[root@mail ~]# ip addr show | grep "inet\b" | awk '{print $2}'
127.0.0.1/8
10.231.8.147/24
#2
[root@mail ~]# echo "I am error message" 1>&2
I am error message
建立一個名為 /root/split 的目錄,進行如下的行為:
- 將 /etc/services 複製到本目錄下
- 假設 services 容量太大了,現在請以 100K 為單位,將該檔案拆解成 file_aa, file_ab, file_ac.. 等檔名的檔案, 每個檔案最大為 100K (請自行 man split 去處理)
[root@mail ~]# mkdir /root/split
[root@mail ~]# split -b 100k -d /etc/services /root/split/file_