1、bash shellhtml
能夠理解爲一種解釋器和啓動器,解釋命令文本,並執行命令。java
命令來源:python
1.示例,寫一個最簡單的文本nginx
vi test.txt
寫入如下內容:chrome
echo "Hello World" ls -l / echo $$
這樣就寫好了一個最簡單的腳本,其中$$表示運行當前腳本的bash的進程號。shell
2.如何運行這個文本中的命令express
# 使用bash內建命令source來運行 source test.txt # 使用"."來運行,注意點後面是空格 . test.txt
實際上source和"."都是bash的biuldin命令:centos
[root@centos-clone1 ~]# type source source is a shell builtin [root@centos-clone1 ~]# type '.' . is a shell builtin
source和"."是等效的,意思是解釋文本中的命令,並執行。咱們常用的source /etc/profile就是讓系統從新執行一下/etc/profile配置腳本。bash
3.使用bash子進程運行cookie
上面使用source和"."來執行腳本,echo $$顯示的是當前bash的進程號。
先使用pstree工具查看進程樹:
[root@centos-clone1 ~]# pstree
-bash: pstree: command not found
發現Mini版的CentOS默認沒有安裝pstree,使用yum安裝:
yum install psmisc -y
安裝完畢後執行pstree:
[root@centos-clone1 ~]# pstree systemd鈹€鈹攢agetty 鈹溾攢auditd鈹€鈹€鈹€{auditd} 鈹溾攢chronyd 鈹溾攢crond 鈹溾攢dbus-daemon 鈹溾攢irqbalance 鈹溾攢lvmetad 鈹溾攢master鈹€鈹攢pickup 鈹 鈹斺攢qmgr 鈹溾攢nginx鈹€鈹€鈹€nginx 鈹溾攢polkitd鈹€鈹€鈹€6*[{polkitd}] 鈹溾攢rsyslogd鈹€鈹€鈹€2*[{rsyslogd}] 鈹溾攢sshd鈹€鈹€鈹€sshd鈹€鈹攢bash鈹€鈹€鈹€pstree 鈹 鈹斺攢bash 鈹溾攢systemd-journal 鈹溾攢systemd-logind 鈹溾攢systemd-udevd 鈹斺攢tuned鈹€鈹€鈹€4*[{tuned}]
發現顯示亂碼,解決他:
vi /etc/sysconfig/i18n
寫入下面內容:
LANG="en_US" SUPPORTED="en_US:en" SYSFONT="latarcyrheb-sun16"
使其生效(臨時生效,從新登陸會失效):
source /etc/sysconfig/i18n
而後再次執行pstree:
[root@centos-clone1 ~]# pstree systemd-+-agetty |-auditd---{auditd} |-chronyd |-crond |-dbus-daemon |-irqbalance |-lvmetad |-master-+-pickup | `-qmgr |-nginx---nginx |-polkitd---6*[{polkitd}] |-rsyslogd---2*[{rsyslogd}] |-sshd-+-sshd---bash | `-sshd---bash---pstree |-systemd-journal |-systemd-logind |-systemd-udevd `-tuned---4*[{tuned}]
紅色部分顯示咱們的pstree運行在一個bash下。
咱們使用命令建立一個子程序bash,並再次使用pstree查看:
[root@centos-clone1 ~]# /bin/bash [root@centos-clone1 ~]# pstree systemd-+-agetty |-auditd---{auditd} |-chronyd |-crond |-dbus-daemon |-irqbalance |-lvmetad |-master-+-pickup | `-qmgr |-nginx---nginx |-polkitd---6*[{polkitd}] |-rsyslogd---2*[{rsyslogd}] |-sshd-+-sshd---bash | `-sshd---bash---bash---pstree |-systemd-journal |-systemd-logind |-systemd-udevd `-tuned---4*[{tuned}]
咱們能夠看到,此次的pstree實在新建立的bash子進程上運行的。
此時,咱們有兩層bash,咱們可使用exit退出一層:
[root@centos-clone1 ~]# exit
exit
若是自己是最底層的bash,若是使用exit,則會logout。
咱們就能夠在建立bash子進程的時候就運行腳本:
[root@centos-clone1 ~]# /bin/bash test.txt hello World total 34 lrwxrwxrwx 1 root root 7 Oct 14 20:48 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Oct 15 12:59 boot drwxr-xr-x 19 root root 3080 Oct 15 13:49 dev drwxr-xr-x. 76 root root 8192 Oct 22 15:21 etc drwxr-xr-x. 5 root root 40 Oct 20 15:57 home lrwxrwxrwx 1 root root 7 Oct 14 20:48 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 14 20:48 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 media116980
咱們發現子進程的PID號是116980,這是咱們剛剛建立並運行test.txt的子進程號。
可是咱們運行echo $$看看:
[root@centos-clone1 ~]# echo $$ 116262
發現當前的PID並非116980,說明一個問題,使用/bin/bash來建立子進程運行腳本,在運行完畢後,會結束子進程,回到父進程。
並且每次使用bash來運行腳本,運行時的子進程PID均可能是不同的。
4.bash腳本
咱們在前面看到,使用/bin/bash運行一個命令文本,咱們每次運行時都須要使用/bin/bash test.txt來運行。
咱們能夠採用如下形式,將該文本默認使用/bin/bash執行,將文本內容修改成:
#!/bin/bash echo "hello world" ls -l / echo $$
咱們在前面加了一行"#!/bin/bash",指定該文本運行的解釋器。這樣直接運行文件就能夠執行了:
# 將文本的權限修改成可執行 chmod u+x test.txt #運行 ./test.txt
[root@centos-clone1 ~]# ./test.txt hello World total 34 lrwxrwxrwx 1 root root 7 Oct 14 20:48 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Oct 15 12:59 boot drwxr-xr-x 19 root root 3080 Oct 15 13:49 dev drwxr-xr-x. 76 root root 8192 Oct 22 15:21 etc drwxr-xr-x. 5 root root 40 Oct 20 15:57 home lrwxrwxrwx 1 root root 7 Oct 14 20:48 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 14 20:48 lib64 -> usr/lib64 ... 119996
同理,python腳本也是同樣的,在開頭寫上"#!/usr/bin/python"等python命令的path。
在這種腳本的執行方式下,實際上和/bin/bash test.txt同樣,是建立了一個子進程來執行,執行完畢後,退出子進程回到父進程。
5.一個重要的問題:爲何要在子進程中去執行腳本(重要)
若是咱們的執行腳本有問題,可能致使進程崩潰,那麼若是在當前進程執行的話,可能致使bash崩潰。
而咱們若是每次執行腳本都fork出一個子進程執行,那麼就算腳本有問題,崩潰的也是子進程,退出後回到父進程,還能夠正常運行。
2、腳本中的函數
shell腳本也支持函數。定義好的函數就至關於一個命令。
一個命令只有如下三種狀況:
1.在交互命令行下定義一個函數
[root@centos-clone1 ~]# ooxx(){ > echo "Hello World" > ls -l / > echo $$ > }
ooxx是函數名,後面跟一個小括號,不用寫參數,只是一個形式。而後接大括號,中間每一行都是一個命令。
運行函數:
[root@centos-clone1 ~]# ooxx Hello World total 34 lrwxrwxrwx 1 root root 7 Oct 14 20:48 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Oct 15 12:59 boot drwxr-xr-x 19 root root 3080 Oct 15 13:49 dev drwxr-xr-x. 76 root root 8192 Oct 22 15:21 etc drwxr-xr-x. 5 root root 40 Oct 20 15:57 home lrwxrwxrwx 1 root root 7 Oct 14 20:48 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 14 20:48 lib64 -> usr/lib64 ...... 116262
直接使用函數名運行,定義好的函數至關於一個命令。
查看該函數的信息:
[root@centos-clone1 ~]# type ooxx ooxx is a function ooxx () { echo "Hello World"; ls --color=auto -l /; echo $$ }
能夠看到ooxx是一個方法,並給出具體方法體。
3、重定向
首先重定向不是命令!!
每一個程序都有I/O:
1.示例,查看程序I/O位置
Linux一切皆文件,I/O也是抽象成文件的。
首先,咱們來查看一下bash的I/O。
查看當前bash的進程號:
[root@centos-clone1 ~]# echo $$ 1594
查看bash進程的I/O文件:
[root@centos-clone1 fd]# cd /proc/$$/fd [root@centos-clone1 fd]# ll total 0 lrwx------ 1 root root 64 Oct 24 01:05 0 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 1 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 2 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 22:25 255 -> /dev/pts/0
$$表示當前bash的進程PID,爲1594。在系統運行的時候,全部程序都會在/proc中抽象出一個文件夾,裏面存放的是程序的運行數據。
在/proc中找到名爲1594的文件夾,這個文件夾就是bash的運行數據,裏面有一個叫fd的文件夾(fd表明文件描述符),其中的0、一、2就是他的標準輸入、標準輸出】錯誤輸出。
這兩個I/O都是默認指向/dev/pts/0的,也就是命令行終端。也就是爲何咱們使用命令的時候,結果會顯示在當前終端屏幕上的緣由。
2.開啓第2個終端
當咱們使用ssh連接第二個終端(多用戶登陸)時,新的用戶也會啓動一個新的bash程序:
[root@centos-clone1 ~]# echo $$ 63926
查看新終端bash的I/O文件:
[root@centos-clone1 ~]# cd /proc/63926/fd [root@centos-clone1 fd]# ll total 0 lrwx------ 1 root root 64 Oct 24 22:29 0 -> /dev/pts/1 lrwx------ 1 root root 64 Oct 24 22:29 1 -> /dev/pts/1 lrwx------ 1 root root 64 Oct 24 22:29 2 -> /dev/pts/1 lrwx------ 1 root root 64 Oct 24 22:30 255 -> /dev/pts/1
咱們發現他的標準輸入輸出都是指向/dev/pts/1的,說明每一個終端都會對應一個輸入輸出文件(對應終端)。
咱們在設備信息中能夠看到這兩個終端的I/O設備:
[root@centos-clone1 pts]# cd /dev/pts/ [root@centos-clone1 pts]# ll total 0 crw--w---- 1 root tty 136, 0 Oct 24 22:25 0 crw--w---- 1 root tty 136, 1 Oct 24 2019 1 c--------- 1 root root 5, 2 Oct 24 01:02 ptmx
在/dev/pts中,咱們看到0、1兩個I/O設備,對應的就是咱們啓動的兩個終端,對應兩個bash進程。
3.初試重定向
咱們能夠將1號終端的標準輸出重定向到2號終端,達到的效果是,1號終端運行"ls -l /"命令,返回的結果在2號終端顯示。
首先,咱們備份1號終端的標準輸出,以便於恢復:
[root@centos-clone1 fd]# cd /proc/$$/fd [root@centos-clone1 fd]# ll total 0 lrwx------ 1 root root 64 Oct 24 01:05 0 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 1 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 2 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 22:25 255 -> /dev/pts/0 [root@centos-clone1 fd]# exec 6>&1 [root@centos-clone1 fd]# ll total 0 lrwx------ 1 root root 64 Oct 24 01:05 0 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 1 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 2 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 22:25 255 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 6 -> /dev/pts/0
而後,將標準輸出1,指向/dev/pts/1文件:
[root@centos-clone1 fd]# exec 1> /dev/pts/1
此時,終端1的標準輸出已經重定向到終端2的輸入設備:
# 終端1中執行ls -l [root@centos-clone1 fd]# ls -l /usr [root@centos-clone1 fd]#
終端1中什麼都沒有顯示,觀察終端2:
# 終端2中顯示了/usr中全部的內容列表
total 128 dr-xr-xr-x. 2 root root 20480 Oct 23 21:20 bin drwxr-xr-x. 2 root root 6 Apr 11 2018 etc drwxr-xr-x. 2 root root 6 Apr 11 2018 games drwxr-xr-x. 42 root root 8192 Oct 21 16:03 include drwxr-xr-x 3 root root 51 Oct 22 14:56 java dr-xr-xr-x. 27 root root 4096 Oct 21 16:00 lib dr-xr-xr-x. 38 root root 20480 Oct 21 16:03 lib64 drwxr-xr-x. 22 root root 4096 Oct 21 16:00 libexec drwxr-xr-x. 13 root root 4096 Oct 21 23:35 local dr-xr-xr-x. 2 root root 16384 Oct 23 21:20 sbin drwxr-xr-x. 76 root root 4096 Oct 21 16:03 share drwxr-xr-x. 4 root root 32 Apr 11 2018 src lrwxrwxrwx 1 root root 10 Oct 14 20:48 tmp -> ../var/tmp
實現完畢後,將終端1的標準輸出流恢復回去:
[root@centos-clone1 fd]# exec 1>&6 [root@centos-clone1 fd]# ll total 0 lrwx------ 1 root root 64 Oct 24 01:05 0 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 1 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 2 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 22:25 255 -> /dev/pts/0 lrwx------ 1 root root 64 Oct 24 01:05 6 -> /dev/pts/0
4.重定向輸出到文件
將ls -l /usr的輸出重定向到文件中:
[root@centos-clone1 ~]# ls -l /usr 1> ~/usr_list [root@centos-clone1 ~]# cat usr_list
total 128 dr-xr-xr-x. 2 root root 20480 Oct 23 21:20 bin drwxr-xr-x. 2 root root 6 Apr 11 2018 etc drwxr-xr-x. 2 root root 6 Apr 11 2018 games drwxr-xr-x. 42 root root 8192 Oct 21 16:03 include drwxr-xr-x 3 root root 51 Oct 22 14:56 java dr-xr-xr-x. 27 root root 4096 Oct 21 16:00 lib dr-xr-xr-x. 38 root root 20480 Oct 21 16:03 lib64 drwxr-xr-x. 22 root root 4096 Oct 21 16:00 libexec drwxr-xr-x. 13 root root 4096 Oct 21 23:35 local dr-xr-xr-x. 2 root root 16384 Oct 23 21:20 sbin drwxr-xr-x. 76 root root 4096 Oct 21 16:03 share drwxr-xr-x. 4 root root 32 Apr 11 2018 src lrwxrwxrwx 1 root root 10 Oct 14 20:48 tmp -> ../var/tmp
若是進行第2次命令,也重定向到該文件,則內容會被覆蓋:
[root@centos-clone1 ~]# ls -l / 1> ~/usr_list [root@centos-clone1 ~]# cat usr_list total 32 lrwxrwxrwx 1 root root 7 Oct 14 20:48 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Oct 15 12:59 boot drwxr-xr-x 19 root root 3080 Oct 24 01:02 dev drwxr-xr-x. 76 root root 8192 Oct 24 01:02 etc drwxr-xr-x. 5 root root 40 Oct 20 15:57 home lrwxrwxrwx 1 root root 7 Oct 14 20:48 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 14 20:48 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 media drwxr-xr-x. 2 root root 6 Apr 11 2018 mnt drwxr-xr-x. 2 root root 6 Apr 11 2018 opt dr-xr-xr-x 115 root root 0 Oct 24 01:02 proc dr-xr-x---. 8 root root 4096 Oct 24 22:42 root drwxr-xr-x 23 root root 640 Oct 24 01:02 run lrwxrwxrwx 1 root root 8 Oct 14 20:48 sbin -> usr/sbin drwxrwx--- 2 root leoshare 18 Oct 20 16:20 share drwxr-xr-x. 2 root root 6 Apr 11 2018 srv dr-xr-xr-x 13 root root 0 Oct 24 01:02 sys drwxrwxrwt. 9 root root 4096 Oct 24 03:44 tmp drwxr-xr-x. 14 root root 4096 Oct 22 14:55 usr drwxr-xr-x. 19 root root 4096 Oct 14 20:48 var
因此">"符號叫作覆蓋重定向。
若是想要追加,則使用">>"符號:
[root@centos-clone1 ~]# ls -l / 1>> ~/usr_list
5.錯誤輸出
每一個程序除了有標準輸入和標準輸出,還有錯誤輸出,也就是2指向的文件。
在Bash的錯誤輸出默認是輸出到終端的,也就是/dev/pts/下的0、1等設備。
#god目錄不存在的狀況 [root@centos-clone1 ~]# ls -l /god ls: cannot access /god: No such file or directory
此時返回的錯誤信息就是錯誤輸出。
將錯誤輸出重定向到文件(使用"2>"符號):
[root@centos-clone1 ~]# ls -l /god 2> err.log [root@centos-clone1 ~]# cat err.log ls: cannot access /god: No such file or directory
6.重定向的綁定順序
示例:
# /god目錄不存在,/usr目錄存在 ls -l /god /usr 2>&1 1> a.out
[root@centos-clone1 ~]# ls -l /god /usr 2>&1 1> a.out ls: cannot access /god: No such file or directory [root@centos-clone1 ~]# cat a.out /usr: total 128 40 dr-xr-xr-x. 2 root root 20480 Oct 23 21:20 bin 0 drwxr-xr-x. 2 root root 6 Apr 11 2018 etc 0 drwxr-xr-x. 2 root root 6 Apr 11 2018 games 12 drwxr-xr-x. 42 root root 8192 Oct 21 16:03 include 0 drwxr-xr-x 3 root root 51 Oct 22 14:56 java 4 dr-xr-xr-x. 27 root root 4096 Oct 21 16:00 lib 40 dr-xr-xr-x. 38 root root 20480 Oct 21 16:03 lib64 4 drwxr-xr-x. 22 root root 4096 Oct 21 16:00 libexec 4 drwxr-xr-x. 13 root root 4096 Oct 21 23:35 local 20 dr-xr-xr-x. 2 root root 16384 Oct 23 21:20 sbin 4 drwxr-xr-x. 76 root root 4096 Oct 21 16:03 share 0 drwxr-xr-x. 4 root root 32 Apr 11 2018 src 0 lrwxrwxrwx 1 root root 10 Oct 14 20:48 tmp -> ../var/tmp
上述運行結果中,/usr的內容列表被保存在了a.out中,而ls -l /god的錯誤信息打印在了屏幕上。
說明錯誤輸出流沒有綁定生效,咱們將2個綁定換一下位置:
ls -l /god /usr 1> a.out 2>&1
[root@centos-clone1 ~]# ls -l /god /usr 1> a.out 2>&1 [root@centos-clone1 ~]# cat a.out ls: cannot access /god: No such file or directory /usr: total 128 40 dr-xr-xr-x. 2 root root 20480 Oct 23 21:20 bin 0 drwxr-xr-x. 2 root root 6 Apr 11 2018 etc 0 drwxr-xr-x. 2 root root 6 Apr 11 2018 games 12 drwxr-xr-x. 42 root root 8192 Oct 21 16:03 include 0 drwxr-xr-x 3 root root 51 Oct 22 14:56 java 4 dr-xr-xr-x. 27 root root 4096 Oct 21 16:00 lib 40 dr-xr-xr-x. 38 root root 20480 Oct 21 16:03 lib64 4 drwxr-xr-x. 22 root root 4096 Oct 21 16:00 libexec 4 drwxr-xr-x. 13 root root 4096 Oct 21 23:35 local 20 dr-xr-xr-x. 2 root root 16384 Oct 23 21:20 sbin 4 drwxr-xr-x. 76 root root 4096 Oct 21 16:03 share 0 drwxr-xr-x. 4 root root 32 Apr 11 2018 src 0 lrwxrwxrwx 1 root root 10 Oct 14 20:48 tmp -> ../var/tmp
此時,咱們發現標準輸出和錯誤輸出都輸出到了a.out。
以上實驗說明重定向綁定的順序是從左到右的。
標準輸出和錯誤輸出定位到一個文件的特殊寫法:
# 二者同樣的效果,都是將一、2都定向到a.out [root@centos-clone1 ~]# ls -l /god /usr >& a.out [root@centos-clone1 ~]# ls -l /god /usr &> a.out
7.輸入重定向
輸入重定向有三種方式:
read命令:接收標準輸入的字符串,以換行符結果,相似python的input:
[root@centos-clone1 ~]# read var1 123abcd [root@centos-clone1 ~]# echo $var1 123abcd
咱們能夠用如下形式重定向輸入:
[root@centos-clone1 ~]# read var1 0<<<"abcd123" [root@centos-clone1 ~]# echo $var1 abcd123
使用三個"<"表示將一行字符串重定向到輸入。
重定向一大段文本到輸入:
[root@centos-clone1 ~]# read var2 0<<ooxx > abc > 123 > uuiery > sdfj > ooxx [root@centos-clone1 ~]# echo $var2 abc
使用兩個"<"能夠將多行文本重定向到輸入,以ooxx做爲整段文本的結束字符。
可是咱們在打印$var2時,發現只有abc三個字符,這是由於read命令對換行符敏感,在輸入流中確實有咱們輸入的全部字符,可是read只讀到了第一行。
此時,咱們將read換爲cat,就正確了:
[root@centos-clone1 ~]# cat 0<<ooxx > abc > 123 > skdfjksf > ooxx abc 123 skdfjksf
咱們能夠看到,cat正確讀到了除了ooxx(咱們定義的結束符)之外的全部行內容。
將文件重定向到標準輸入:
[root@centos-clone1 ~]# cat 0< myname.txt My name is Leo...
這裏只是一個示例,沒有實際應用價值,由於上述命令等效於cat myname.txt。
8.一個綜合的重定向示例(經過重定向請求www.baidu.com首頁)
咱們使用exec定義一個TCP socket:
# 將描述符8指向tcp socket,與baidu創建鏈接 [root@centos-clone1 fd]# exec 8<> /dev/tcp/www.baidu.com/80 # 向8發送http請求頭 [root@centos-clone1 fd]# echo -e "GET / HTTP/1.0\n" 1>&8 # 從8接收http響應 [root@centos-clone1 fd]# cat 0<&8
咱們發現,www.baidu.com返回的響應打印在屏幕上:
[root@centos-clone1 fd]# cat 0<&8 HTTP/1.0 200 OK Accept-Ranges: bytes Cache-Control: no-cache Content-Length: 14615 Content-Type: text/html Date: Fri, 25 Oct 2019 06:38:48 GMT P3p: CP=" OTI DSP COR IVA OUR IND COM " P3p: CP=" OTI DSP COR IVA OUR IND COM " Pragma: no-cache Server: BWS/1.1 Set-Cookie: BAIDUID=160ACF42CC151F0ED2B6D3241C0FE02A:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com Set-Cookie: BIDUPSID=160ACF42CC151F0ED2B6D3241C0FE02A; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com Set-Cookie: PSTM=1571985528; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com Set-Cookie: BAIDUID=160ACF42CC151F0EA49EFE62D0FE9DB0:FG=1; max-age=31536000; expires=Sat, 24-Oct-20 06:38:48 GMT; domain=.baidu.com; path=/; version=1; comment=bd Traceid: 157198552802713482341501073886667258581 Vary: Accept-Encoding X-Ua-Compatible: IE=Edge,chrome=1 ...... ...... ...... http:\/\/.+\.baidu\.com'))[0];}}name && ns_c({'fm': 'behs','tab': name,'query': encodeURIComponent(key),'un': encodeURIComponent(bds.comm.user || '') });};}})();};if(window.pageState==0){initIndex();}})();document.cookie = 'IS_STATIC=1;expires=' + new Date(new Date().getTime() + 10*60*1000).toGMTString();</script> </body></html>
若是咱們想存入文件,在使用cat讀取響應的時候,將輸出重定向到文件就能夠了:
[root@centos-clone1 fd]# exec 8<> /dev/tcp/www.baidu.com/80 [root@centos-clone1 fd]# echo -e "GET / HTTP/1.0\n" 1>&8 [root@centos-clone1 fd]# cat 0<&8 1> ~/baidu.txt [root@centos-clone1 fd]# cat ~/baidu.txt
注意:若是在建立了socket後,咱們過一段時間才發送請求,可能由於socket連接超時而收不到響應,此時應該從新綁定8>/dev/tcp/www..baidu.com/80。
2、變量
1.變量生命週期
變量的生命週期是在當前運行bash的進程中的,當前bash退出後,變量也就消亡了。
[root@centos-clone1 ~]# myname=Leo [root@centos-clone1 ~]# echo $myname Leo [root@centos-clone1 ~]# /bin/bash [root@centos-clone1 ~]# echo $myname [root@centos-clone1 ~]# exit exit [root@centos-clone1 ~]# echo $myname Leo
咱們能夠看到,定義一個變量myname爲Leo,當前進程中打印$myname輸出Leo正確。
咱們啓動一個bash子進程,並在子進程中打印$myname,輸出空,表示沒有這個變量。
退出子進程回到父進程,再次打印$myname,正確顯示Leo。
2.局部變量
在函數內部,咱們能夠定義局部變量:
[root@centos-clone1 ~]# ooxx(){ > local age=32 > echo $age > } [root@centos-clone1 ~]# ooxx 32 [root@centos-clone1 ~]# echo $age
#輸出空
能夠看到在函數內部定義的局部變量的生命週期只是在函數內部,函數執行完後,局部變量消亡。
3.函數內部訪問普通變量
[root@centos-clone1 ~]# ooxx(){ > echo $myname > myname=John > echo $myname > } [root@centos-clone1 ~]# ooxx Leo John [root@centos-clone1 ~]# echo $myname John
能夠看到,函數內部能夠訪問普通變量,並能夠修改其值。
4.參數
對於shell腳本和shell函數,咱們都須要傳入參數。
和JAVA,C++等高級語言不一樣,shell的參數不須要形參,而是經過一些符號來取值:
[root@centos-clone1 ~]# ooxx(){ > echo $1 > echo $2 > echo $# > } [root@centos-clone1 ~]# ooxx 1 2 3 4 5 1 2 5
咱們能夠看到,shell中的參數是使用$一、$二、$#、$*等來取值的,以下表所示:
# 參數個數 $# # 參數列表 $* # 參數列表 $@ # 第一個參數 $1 # 第二個參數 $2 # 第11個參數 ${11}
使用示例:
# 建立一個腳本 vi test.sh
# 輸入內容 echo $1 echo $2 echo $11 echo ${11} echo $# echo $* echo $@
運行腳本:
[root@centos-clone1 ~]# . test.sh 1 2 3 4 5 6 7 8 9 0 a b c 1 2 11 a 13 1 2 3 4 5 6 7 8 9 0 a b c 1 2 3 4 5 6 7 8 9 0 a b c
咱們輸入了13個參數
$1爲1正確。$2爲2正確。$11爲11,不正確,是先取了$1而後拼接上1,獲得11。${11}取第11個參數爲a是正確的。
$#輸出13正確,一共有13個參數。$*和$@輸出參數列表。
5.管道
重要:管道符號"|"實際上會開啓多個子進程來完成先後的命令。
[root@centos-clone1 ~]# ls -l / | more
也就是"|"左邊的ls命令和右邊的more命令,會分別新建一個子進程來分別執行。管道打通兩個子進程之間的通道,將ls的輸出傳遞給more命令。
咱們驗證一下:
[root@centos-clone1 ~]# echo $$ 1594 [root@centos-clone1 ~]# echo $$ | more 1594
第一個echo $$打印的是當前進程的PID,而根據上述的說法,第二個命令打印的PID不該該是1594。這裏出現了問題:
咱們將echo $$換一個命令:
[root@centos-clone1 ~]# echo $BASHPID 1594 [root@centos-clone1 ~]# echo $BASHPID | more 113173
使用echo $BASHPID一樣獲取的是進程的PID。可是第二個命令的結果卻真正輸出了子進程的PID。
這是由於,echo $$的優先級高於管道符號"|":
# bash先看到echo $$並將其執行,結果替換爲了1594,而後再啓動子進程 echo $$ | more # 至關於 echo 1594 | more
而echo $BASHPID只是一個普通取值,優先級低於管道符號"|":
# 先啓動了子進程,而後在子進程中執行echo $BASHPID echo $BASHPID | more
# 因此$BASHPID拿到的是子進程的PID
6.判斷一個命令是否執行成功
[root@centos-clone1 ~]# ls -l / total 32 lrwxrwxrwx 1 root root 7 Oct 14 20:48 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 Oct 15 12:59 boot drwxr-xr-x 19 root root 3080 Oct 24 01:02 dev drwxr-xr-x. 76 root root 8192 Oct 24 01:02 etc drwxr-xr-x. 5 root root 40 Oct 20 15:57 home lrwxrwxrwx 1 root root 7 Oct 14 20:48 lib -> usr/lib lrwxrwxrwx 1 root root 9 Oct 14 20:48 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 media drwxr-xr-x. 2 root root 6 Apr 11 2018 mnt drwxr-xr-x. 2 root root 6 Apr 11 2018 opt dr-xr-xr-x 114 root root 0 Oct 24 01:02 proc dr-xr-x---. 8 root root 4096 Oct 25 15:11 root drwxr-xr-x 23 root root 640 Oct 24 01:02 run lrwxrwxrwx 1 root root 8 Oct 14 20:48 sbin -> usr/sbin drwxrwx--- 2 root leoshare 18 Oct 20 16:20 share drwxr-xr-x. 2 root root 6 Apr 11 2018 srv dr-xr-xr-x 13 root root 0 Oct 24 22:44 sys drwxrwxrwt. 9 root root 4096 Oct 25 14:30 tmp drwxr-xr-x. 14 root root 4096 Oct 22 14:55 usr drwxr-xr-x. 19 root root 4096 Oct 14 20:48 var [root@centos-clone1 ~]# echo $? 0 [root@centos-clone1 ~]# ls -l /god ls: cannot access /god: No such file or directory [root@centos-clone1 ~]# echo $? 2
咱們能夠看到,當ls執行成功時,$?的值爲0。
當ls執行失敗時,$?的值爲非0。
因此,咱們在腳本中能夠經過判斷$?來判斷前面的命令是否執行成功。
3、環境變量
前面咱們提到的變量的生命週期都是限於當前進程的。
咱們的父進程要建立一個子進程,而子進程也要獲取到某個變量的值(環境變量)。
什麼叫作環境變量,環境多是包含多個進程的一個環境,則環境變量可以供多個進程訪問。
[root@centos-clone1 ~]# echo $env_var 999 [root@centos-clone1 ~]# /bin/bash [root@centos-clone1 ~]# echo $env_var 999
使用export將變量導出,則能夠供子進程訪問。
注意:這裏的export是導出而不是共享,也就是說該變量可供該父進程建立的全部子進程訪問,可是不是共享的。當子進程修改該變量時,父進程中這個變量的值不發生改變。反之同理。
重要:export導出的變量,至關於在子進程中有一個拷貝,各自擁有一個獨立的副本。
在這種原理下,咱們考慮一個場景:
當父進程中有大量的導出的環境變量,每一個變量的值佔用空間很大。全部環境變量加起來例若有10GB,那麼父進程建立子進程時,理論上會將全部的環境變量進行拷貝。子進程建立速度會很是緩慢。
操做系統內核設計者如何解決這個問題,答案時使用copy on write技術。
即,建立子進程時,全部的環境變量以引用的方式傳遞給子進程,當父進程要修改某個環境變量時,在修改以前,將副本拷貝給子進程,這樣將10G的變量分時拷貝,就能夠解決這個問題。
一樣的,當子進程要修改某個變量時,則先拷貝該變量,而後再進行修改。
4、其餘一些知識點
1.引用
雙引號引用:
[root@centos-clone1 ~]# name=Leo [root@centos-clone1 ~]# echo $name Leo [root@centos-clone1 ~]# echo "$name say hello" Leo say hello
雙引號能夠拼接中間帶空格的字符串。而且其中的$name優先級更好,bash會取到name的值。
單引號引用:
[root@centos-clone1 ~]# name=Leo [root@centos-clone1 ~]# echo $name Leo [root@centos-clone1 ~]# echo '$name say hello' $name say hello
咱們發現單引號中的$name沒法取值,說明$取值的優先級低於單引號。
2.將命令輸出賦值給變量
[root@centos-clone1 ~]# lines=`ls -l / | wc -l` [root@centos-clone1 ~]# echo $lines 21
整個命令用反引號引發來。
5、表達式
1.算術表達式
第一種形式:let c=$a+$b
[root@centos-clone1 ~]# a=1 [root@centos-clone1 ~]# b=2 [root@centos-clone1 ~]# let c=$a+$b [root@centos-clone1 ~]# echo $c 3
第二種形式:c=$((a+b))
[root@centos-clone1 ~]# c=$((a+b)) [root@centos-clone1 ~]# echo $c 3
2.條件表達式(邏輯表達式)
使用test作條件判斷,查看help test:
[root@centos-clone1 ~]# help test test: test [expr] Evaluate conditional expression. File operators: -a FILE True if file exists. -b FILE True if file is block special. -c FILE True if file is character special. -d FILE True if file is a directory. -e FILE True if file exists. -f FILE True if file exists and is a regular file. -g FILE True if file is set-group-id. -h FILE True if file is a symbolic link. -L FILE True if file is a symbolic link. -k FILE True if file has its `sticky' bit set. -p FILE True if file is a named pipe. -r FILE True if file is readable by you. -s FILE True if file exists and is not empty. -S FILE True if file is a socket. -t FD True if FD is opened on a terminal. -u FILE True if the file is set-user-id. -w FILE True if the file is writable by you. -x FILE True if the file is executable by you. -O FILE True if the file is effectively owned by you. -G FILE True if the file is effectively owned by your group. -N FILE True if the file has been modified since it was last read. FILE1 -nt FILE2 True if file1 is newer than file2 (according to modification date). FILE1 -ot FILE2 True if file1 is older than file2. FILE1 -ef FILE2 True if file1 is a hard link to file2. String operators: -z STRING True if string is empty. -n STRING STRING True if string is not empty. STRING1 = STRING2 True if the strings are equal. STRING1 != STRING2 True if the strings are not equal. STRING1 < STRING2 True if STRING1 sorts before STRING2 lexicographically. STRING1 > STRING2 True if STRING1 sorts after STRING2 lexicographically. Other operators: -o OPTION True if the shell option OPTION is enabled. -v VAR True if the shell variable VAR is set ! EXPR True if expr is false. EXPR1 -a EXPR2 True if both expr1 AND expr2 are true. EXPR1 -o EXPR2 True if either expr1 OR expr2 is true. arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne, -lt, -le, -gt, or -ge.
能夠從幫助中看到,test能夠作文件狀態的判斷,文件之間的比較,以及字符串的比較,數值的比較等等。
使用test比較兩個數字:
[root@centos-clone1 ~]# test 3 -gt 8 [root@centos-clone1 ~]# echo $? 1
3大於8,結果應該是假,因此$?的值爲非0。
[root@centos-clone1 ~]# test 3 -lt 8 [root@centos-clone1 ~]# echo $? 0
反之,命令的執行狀態爲0,表示結果爲真。
因爲運行的結果是以命令成敗與否來判斷,則通常使用如下形式:
[root@centos-clone1 ~]# test 3 -gt 8 && echo "It's true"
當前面成功運行時,後面的纔會執行。
也能夠寫成更清晰的形式:
[root@centos-clone1 ~]# [ 3 -gt 8 ] && echo "It's true"
咱們看一下"[]"的信息:
[root@centos-clone1 ~]# type '[' [ is a shell builtin
發現[]是一個bash內建命令,是命令後面就必須跟一個空格。。。
[root@centos-clone1 ~]# [3 -gt 8 ] && echo "It's true" bash: [3: command not found [root@centos-clone1 ~]# [ 3 -gt 8] && echo "It's true" bash: [: missing `]'
前中括號的後面以及後中括號的前面都必要帶空格,不然會報錯。
總結:只要是命令,後面必須有空格。
若是要取反:
[root@centos-clone1 ~]# [ ! 3 -gt 8 ] && echo "It's true"
取反就在前面加"!"。
6、示例(編寫一個自動建立用戶的腳本)
#!/bin/bash [ $# -eq 2 ] || exit 2 adduser $1 echo $2 | passwd --stdin $1 &> /dev/null echo "Add User Seccuss..."
解釋:
1)[ $# -eq 2 ] || exit 2,表示判斷參數是不是2個,若是不是2個,則退出腳本。exit後面跟數字標準腳本的執行狀態,0表示正確退出,1-127表示錯誤。
2) 建立用戶,用戶名從第一個參數獲取
3) 設置用戶密碼,從第二個參數獲取,注意這裏passwd命令要接受標準輸入必須使用--stdin選項。並將命令的返回信息所有扔掉,/dev/null是一個數據黑洞。
4) 打印一個消息,表示添加成功。
添加用戶是否存在的驗證:
[root@centos-clone1 ~]# id leokale uid=1003(leokale) gid=1004(leokale) groups=1004(leokale) [root@centos-clone1 ~]# echo $? 0
當用戶存在時id命令執行正確,返回0。
[root@centos-clone1 ~]# id leokale id: leokale: no such user [root@centos-clone1 ~]# echo $? 1
當用戶不存在時,id命令執行失敗,返回非0數字1。
因此,咱們能夠經過id命令的執行狀態來判斷該用戶是否存在:
#!/bin/bash [ ! $# -eq 2 ] && echo "number of args wrong.." && exit 2 id $1 &> /dev/null && echo "User is exist..." && exit 5 adduser $1 echo $2 | passwd --stdin $1 &> /dev/null echo "Add User Seccuss..."
經過id命令判斷指定建立的用戶是否存在,若是存在,則提示用戶存在,而後錯誤退出,退出碼爲5。若是用戶不存在,則執行後面的邏輯,建立指定用戶。
只有root用戶纔有權限添加用戶,咱們要考慮普通用戶誤運行該腳本的狀況:
#!/bin/bash [ ! $# -eq 2 ] && echo "number of args wrong.." && exit 2 id $1 &> /dev/null && echo "User is exist..." && exit 5 adduser $1 &>/dev/null && echo $2 | passwd --stdin $1 &> /dev/null && echo "Add User Seccuss..." $$ exit 0 echo "Somewhere wrong.." exit 7
咱們將建立用戶,建立密碼都用&&串起來,這樣當建立用戶失敗的狀況下,橫向命令都不會繼續執行,而後錯誤退出,退出碼爲7。
# 使用普通用戶leo來執行腳本
[leo@centos-clone1 tmp]$ ./addUser number of args wrong.. [leo@centos-clone1 tmp]$ ./addUser leokale 52myself User is exist... [leo@centos-clone1 tmp]$ ./addUser sjdjkf sdf Somewhere wrong..
7、控制語句
1.if判斷語句
查看if的幫助:
[leo@centos-clone1 tmp]$ help if if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi Execute commands based on conditional. ...... ...... Exit Status: Returns the status of the last command executed.
咱們能夠看到if語句的基本形式爲:if 命令; then 命令; else 命令; fi。。以if開始,以fi結尾。
if語句使用示例:
[root@centos-clone1 ~]# vi test2.sh if ls -l / &> /dev/null; then echo "OK!"; else echo "Not OK!"; fi
[root@centos-clone1 ~]# vi test2.sh if [ 3 -gt 8 ]; then echo "OK!"; else echo "Not OK!"; fi
分行寫法:
[root@centos-clone1 ~]# vi test2.sh if ls -l / &> /dev/null; then
echo "OK!" else
echo "Not OK!" fi
2.for循環
查看for的幫助:
[root@centos-clone1 ~]# help for for: for NAME [in WORDS ... ] ; do COMMANDS; done Execute commands for each member in a list. ...... for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done Arithmetic for loop. Equivalent to (( EXP1 )) while (( EXP2 )); do COMMANDS (( EXP3 )) done ......
for循環使用示例:
[root@centos-clone1 ~]# vi test3.sh for((i=0;i<10;i++));do echo $i;done
[root@centos-clone1 ~]# chmod +x test3.sh [root@centos-clone1 ~]# ./test3.sh 0 1 2 3 4 5 6 7 8 9
分行寫:
[root@centos-clone1 ~]# vi test3.sh for((i=0;i<10;i++)); do
echo $i done
加強for循環:
[root@centos-clone1 ~]# vi test4.sh for name in leo john "leo kale";do echo $name;done
[root@centos-clone1 ~]# ./test4.sh leo john leo kale
分行寫法:
[root@centos-clone1 ~]# vi test4.sh for name in leo john "leo kale";do echo $name done
3.while循環
[root@centos-clone1 ~]# vi test5.sh while ls -l /god;do echo "ok" rm -rf /god done
[root@centos-clone1 ~]# ./test5.sh total 0 ok ls: cannot access /god: No such file or directory
預先建立一個/god目錄,而後執行上述腳本,循環第一次發現有/god目錄,而後執行刪除操做,第二輪循環發現目錄不存在,則退出。
4.case多分支
[root@centos-clone1 ~]# vi test6.sh name=$1 case $name in "john") echo "John";; "leo") echo "Leo";; *) echo "wrong";; esac
[root@centos-clone1 ~]# ./test6.sh leo Leo [root@centos-clone1 ~]# ./test6.sh john John [root@centos-clone1 ~]# ./test6.sh leokale wrong
8、示例
加強For循環讀取一個文件,打印每一行,並統計行數:
[root@centos-clone1 ~]# vi scanfile.sh #!/bin/bash oldIFS=$IFS IFS=$'\n' num=0 content=`cat testfile.txt` for i in $content;do echo $i ((num++)) done echo "$num lines" IFS=$oldIFS
解釋:
1)首先咱們備份IFS,IFS是一個環境變量,其中定義了作文本分割的符號,例如空格、換行等。
2)將IFS賦值爲只有換行符,不然文件中的每一行若是存在空格,則會被自動切分紅多行。
3)完成後,須要將IFS回覆,不影響其餘程序的運行。
逐步For循環方式:
[root@centos-clone1 ~]# vi scanfile.sh #!/bin/bash num=0 lines=`cat testfile.txt | wc -l` for((i=1;i<=lines;i++));do head -$i testfile.txt | tail -1 ((num++)) done echo num:$num
解釋:
1)使用cat testfile.txt | wc -l獲取文件行數
2) 從第1行開始for循環遍歷,獲取每一行內容,並打印
while循環方式:
[root@centos-clone1 ~]# vi scanfile.sh #!/bin/bash exec 8<$0 exec 0< testfile.txt num=0 while read line;do echo $line ((num++)) done echo num:$num exec 0<&8
解釋:
1)先備份準備輸入
2)重定向標準輸入爲testfile.txt文件
3)使用read從輸入流中讀取一行(read對換行符敏感),循環讀取
4)回覆標準輸入