本文在 linux 命令 的基础上,具体描述 Shell 脚本的相关知识。
1.shell格式 一堆命令写在在同一个文件里。
注意 :第一行 #! 与 shell 之间有空格。
1 2 3 4 5 6 7 #! /bin/sh echo “Our frist an empty line in output” /bin/pwd
2.执行脚本 source 是 bash shell 的内置命令。(与 . 等价)
1 2 3 4 5 6 bash my_shell zsh my_shell ./my_shellsource my_shell . ~/learn_shell/my_shell
3.内建命令 在使用 export PATH=
后,仍能执行的命令就是内建命令。
4.小括号 执行 cd 后,不改变工作目录。
相当于父进程为当前路径,子进程执行该命令。
1 2 (cd ..; ls -al) (cd .. && ls -al)
5.变量 5.1 数据类型 只有一种数据类型:字符串
5.2 变量类型 两种变量:环境变量、本地变量。
环境变量:环境变量存在于所有进程中。环境变量可以从父进程传给子进程,因此 Shell 进程的环境变量可以从当前 Shell 进程传递给 fork 出来的子进程。 5.3 变量导出与删除 本地变量只能在当前 Shell 进程中使用,export 可以将本地变量导出为环境变量,使本地变量在其他进程中可以使用。 1 2 3 4 5 6 aaaa=2export aaaaexport aaaa=2
6.通配符 Wildcard
通配符 含义 * 匹配 0 个或者多个任意字符 ? 匹配 1 个任意字符 [] 匹配方括号中任意一个字符
用途不同
通配符用于通配文件名,正则表达式用于匹配文本内容
使用地点不同
通配符通常只能用于shell,被shell自解释。
正则表达式需要被正则引擎解析,需要用于支持正则表达式的代码或命令中。
元字符不同
通配符只有三个元字符,正则表达式根据正则引擎不同会多种多样。 并且相同元字符表示的含义也可能不同。如通配符中表示匹配任意字符,正则中 表示前面的字符重复0或任意次。
**实例:**使用 grep是字符串命令,需使用正则表达式;文件名通配使用通配符。 1 2 3 4 ls *.txt ls file?.log ls [a-z]*.log ls [^a-z]*.log
反斜杠(\
)或引号('
, "
)都会使通配符失效。
如: \*
, "*"
, '*'
都表示*
本身,不通配任何文件。
7.命令代换 由反引号括起来的是一条命令,Shell 应先执行该命令,再将执行结果代换到当前命令中。 等号两侧无空格。 8.算术代换 算术代换要写两个括号或者一个中括号。
1 2 3 4 5 var=10echo $(($var +3 ))echo $((var+3 ))echo $[var+3]echo $[$var +3]
PS:二进制的100 + 十进制的3
9.转义字符 1 2 3 4 5 6 7 8 9 touch ./---a.txt rm -rf ./---a.txt touch ---a.txt touch -- ---a.txtrm -rf -- ---a.txt
10.引号 引号是字符串的界定符。引号中的内容就是纯粹的字符串。
单引号和双引号区别
双引号防止通配符扩展,但是允许变量扩展。
1 2 echo "$SHELL " echo '$SHELL'
PS:作为好的 Shell 编程习惯,应该总是把变量取值放在双引号内。
11.shell脚本语法 11.1 条件测试 test 参数 含义 -eq 等于 -ne 不等于 -lt 小于 -le 小于等于 -gt 大于 -ge 大于等于
PS:返回 0 表示真,返回 1 表示假。【和其他语言相反】
注意:比较双方只能是整数或者整数变量。
1 2 3 4 5 6 7 8 var=20test $var -gt 100 eoch $?
参数 含义 -d 是否是目录 -f 是否是普通文件 -z stging 是否为零 -n stging 是否为非零 = 两个字符串是否相同 != 两个字符串是否不相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 test -d my_dirtest -f my_fifo var=aatest -z var var1=a var2=btest $var1 = $var2 test 2 = 2
1 2 3 4 5 var1=a var2=b [ $var1 = $var2 ] [ -f my_fifo ]
1 2 3 [ -f my_fifo -a -d my_dir ] [ -f my_fifo -o -d my_dir ]
11.2 模糊匹配 if 1 2 3 4 5 6 7 if [ -f frist_shell ]; then echo "This is common file" elif [ -d frist_shell ]; then echo "This is dir" else printf "unkonw\n" fi
PS:then
可以分行写,这样 ;
不用写,如下所示。
1 2 3 4 5 6 7 8 9 if [ -f frist_shell ]then echo "This is common file" elif [ -d frist_shell ]then echo "This is dir" else printf "unkonw\n" fi
1 if [ -f frist_shell ]; then echo "This is common file" ; else printf "unkonw\n" ; fi
1 2 3 if :; then echo "always true" fi
11.3 精准匹配 case 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #! /bin/zsh echo "Enter a yes or no" read BUFcase "$BUF " in yes |Yes|YES|y|Y) echo "It is a yes" ;; [nN]?) echo "It is a no" ;; *) echo "other case" ;;esac echo "Going to return" return 0
服务 start、stop、status 等均是通过参数来控制,可以用 case 做脚本进行控制。
1 2 3 4 5 6 7 8 9 10 11 12 case "$parameter " in start) ...;; stop) ...;; restart) ...;; status) ...;; *) ...;;esac
11.4 循环 for 类似于 foreach,枚举。
1 2 3 for FRUIT in apple banana pear; do echo "I like $FRUIT " done
1 for FILE_NAME in chap?; do mv "$FILE_NAME " "$FILE_NAME ~" ; done
11.5 循环 while count 自减,循环输出 count 值。
1 2 3 4 5 count=3while [ $count -gt 0 ]; do count=$[count-1] echo "count = $count " done
11.6 break 与 continue break:可以指定跳出几层循环。
continue:跳过本次循环,但不会跳出循环。
12.特殊变量与输入输出 特殊变量是由 Shell 自动赋值的。
自动变量 含义 $0 相当于 c 中的 argv[0] $1、$2… 相当于 c 中的 argv[1]、argv[2]… $# 参数个数,argc - 1 $@ 表示参数列表 $1、$2、$3、$4… 例如可以使用在 for in 后面 $* 同上 $? 上一条命令的 Exit Status $$ 当前进程号
shift 是参数左移出队的操作。
shift 相当于 shift1 shift3:左移出队三个参数 1 2 3 ./my.sh aa bb cc dd ee
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 printf "第一个参数%s 是 %s\n" '$0' "$0 " printf "参数个数%s 是 %s\n" '$#' "$# " echo "循环打印参数列表" for arg in $@ ; do echo "$arg " done ,但双引号允许变量扩展)echo "使用 shift 进行参数左移" shift for arg in $@ ; do echo "$arg " done
参数 含义 echo 默认有一个回车 -e 解析转义字符 -n 不换行
1 2 echo "hello\n" echo -e “hello\n”
13.管道 14.tee 将标准输出同时输出到指定的文件中。
15.文件重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ls > filels >> filels abc.txt > file 2>&1cat < file > new_filewc -l < file > new_filels > /dev/null 2>&1
16.函数 有函数定义,但是没有函数返回值、函数列表。
先定义、再使用。
1 2 3 4 5 6 7 8 foo() { echo "This is function!" }echo "----start----" foo echo "----end----"
使用 $1、$2、$3…来表示函数的参数。
注意:函数内部使用 $1、$2、$3…只能使用函数的参数。(同时 $#、$@ 也是表示函数的参数列表,而不是整个脚本的参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 foo () { echo $0 echo $1 echo $2 echo $3 echo "This is function!" }echo "----start----" foo 11 22 33echo "----end----" ----start---- foo 11 22 33 This is function ! ----end----
**实例:**批量创建文件夹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 is_dir () { dir_name=$1 if [ ! -d $dir_name ]; then return 1; esle return 0; fi }for dir_name in "$@ " ; do if is_dir $dir_name ; then echo “$dir_name is exits.” else echo "$dir_name is not exits,creating it now." mkdir $dir_name > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "error: Cannot create $dir_name ." exit 1; fi fi done
17.调试 shell 脚本 18.正则表达式 查找文件中的内容,字符串匹配(grep,egrep)见基础正则和扩展正则区别 >。
注意:Linux 中使用,一定要使用双引号。
(1)字符类
字符 含义 举例 说明 . 匹配任意一个字符 abc. 匹配 abcd、abc9等 [] 匹配括号中任一字符 [abc] 只能匹配a、b、c - 在[]括号内表示字符范围 [0-9a-fA-F] 匹配一位十六进制的数字 ^ 位于[]括号内的开头,匹配除括号中的字符之外的任意一个字符 [^xy] 匹配除 xy 之外的任一字符,可以匹配 a1、b1,但不能匹配 x1、y1 [[:xxx:]] grep 工具预定义的一些命名字符类 [[:alpha:]] /[[:digit:]] 匹配一个字母 / 匹配一个数字
1 yum list installed | grep .um
1 2 3 4 ➜ ~ yum list installed | grep "[mn]aria" mariadb.x86_64 1:5.5.65-1.el7 @base mariadb-libs.x86_64 1:5.5.65-1.el7 @anaconda mariadb-server.x86_64 1:5.5.65-1.el7 @base
1 2 3 yum list installed | grep "[^yd]um" numactl-libs.x86_64 2.0.12-5.el7 @anaconda perl-Data-Dumper.x86_64 2.145-3.el7 @base
\w
【小写w】匹配文字和数字字符,也就是 [A-Za-z0-9]。\W
【大写W】匹配非文字和数字字符,“\w” 的反置形式。(2)数量限定符
字符 含义 举例 说明 ? 紧跟在它前面的单元应匹配零次或一次 [0-9]?\.[0-9] 可匹配 0.0、2.3、.5 + 紧跟在它前面的单元应匹配一次或多次 [0-9a-zA-Z_.-]+@[0-9a-zA-Z_.-]+\.com 匹配邮箱 * 紧跟在它前面的单元应匹配零次或多次 {N} 紧跟在它前面的单元应匹配 N 次 [1-9][0-9]{2} 匹配100-999的整数 {N,} 紧跟在它前面的单元应匹配至少 N 次 [1-9][0-9]{2,} 匹配大于等于100的数 {,M} 紧跟在它前面的单元应匹配最多 M 次 {N,M} 紧跟在它前面的单元应匹配至少 N 次,最多 M 次 [0-9]{1,3}(.[0-9]{1,3}){3} 匹配 ip,()内重复三次
1 2 3 4 5 6 7 ➜ ~ yum list installed | grep "0\{3\}" iwl1000-firmware.noarch iwl2000-firmware.noarch iwl5000-firmware.noarch iwl6000-firmware.noarch iwl6000g2a-firmware.noarch iwl6000g2b-firmware.noarch
x\{m,\}
匹配 字符 x 重复 m 次或者 m 次以上 的行。[m, +∞)x\{m, n\}
匹配 字符 x 重复 m 次或者 m 次以上,不超过 n 次 的行。 [m, n](3)位置限定符
字符 含义 举例 说明 ^ 匹配行首的位置 ^Content 匹配位于一行开头的 Content
$ 匹配行末的位置 answer:$ 匹配位于一行结尾的 answer:
\< 匹配单词开头的位置 \<ab 匹配 ab 开头的单词 \> 匹配单词结尾的位置 ab\> 匹配 ab 结尾的单词 \b 匹配单词开头或结尾的位置 可以完全取代 \< 和 \> \B 匹配非单词开头和结尾的位置 \Bab\B ab 既不在开头,也不在结尾
1 2 yum list installed | grep ^a grep ^a 1.txt
1 yum list installed | grep a$
1 yum list installed | grep "\<maria"
1 yum list installed | grep "db\>"
1 yum list installed | grep "mariadb"
(4)其他特殊字符
字符 含义 举例 \ 转义字符,将特殊字符转义为普通字符;普通字符转义为特殊字符。 文本中出现 .
需要使用 \.
来进行转义。 普通字符 <> 转义为匹配开头和结尾 \<
\>
。 () 将正则表达式一部分括起来组成一个单元,可以对整个单元使用数量限定符。 [0-9]{1,3}(.[0-9]{1,3}){3} | 连接两个子表达式,表示或的关系。 n(o|either),匹配 no、neither
(5)基础正则和扩展正则 egrep 可以直接使用扩展正则。grep 若要使用扩展正,则有两种方式:
将 ?+{}|() 使用 \ 进行转义。 加 -E 参数,grep -E 1 2 3 egrep "[0-9]{1,3}(\.[0-9]{1,3}){3}" text grep "[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}" text grep -E "[0-9]{1,3}(\.[0-9]{1,3}){3}" text
19.sed 筛选行数据。
19.1 命令格式 sed 参数 ’脚本语句‘ 待操作文件 1 2 sed '/echo/s/echo/printf/g' my_shell.sh sed 's/echo/printf/g' my_shell.sh
echo:在 my_shell.sh 中找到 echo s:表示字符串替换 echo/printf:将 echo 替换成 printf g:一行出现多次 echo,均全部替换 sed 参数 -f ’脚本文件‘ 待操作的文件 19.2 命令参数 参数 含义 -n 静默输出,sed 程序在所有脚本指令执行完毕后,自动打印模式空间中的内容。-n屏蔽自动打印。 -f 从文件中读取脚本指令 -i 直接修改源文件。经过脚本指令处理的内容直接输出至源文件。 -r 使用扩展正则。
19.3 脚本格式 pattern 是正则表达式,action 是编辑操作。
sed 一行行读取待处理文件,如果某一行与 pattern 匹配,则执行相应的 action
如果一条命令没有 pattern,而只有 action,这个 action 将作用于待处理文件的每一行。
举例:
1 2 3 4 5 /pattern/p /pattern/d /pattern/s/pattern1/pattern2/ /pattern/s/pattern1/pattern2/g
19.4 脚本参数 参数 含义 a append 追加 i insert 插入 d delete 删除 s substitution 替换
在 my_shell.sh 的第 4 行后,追加一行,append_line 在第 5 行显示。
1 sed '4a append_line' my_shell.sh
在 my_shell.sh 的第4行,插入一行,insert_line 在第 4 行显示。
1 sed '4i insert_line' my_shell.sh
注意:替换修改源文件需要加参数 -i。
删除 my_shell.sh 的第 4 行。
删除 my_shell.sh 的第 2 到第 7 行。
注意:替换修改源文件需要加参数 -i。
将 my_shell.sh 的 echo 替换成 printf。
1 2 3 4 5 sed 's/echo/printf/g' my_shell.sh
19.5 替代符 在 abc 的两侧加入 ==
。
1 sed 's/abc/==&==/' my_shell.sh
sed 只支持 basic 正则表达式规范。
匹配两个数字,第一个数字部分用 \1 表示,第二个数字数字用 \2 表示,在 \1 和 \2 位置两侧分别加入 == 和 @@。
1 2 3 4 5 6 sed 's/\([0-9]\)\([0-9]\)/==\1==@@\2@@/g' my_shell.sh sed -r 's/([0-9])([0-9])/==\1==@@\2@@/g' my_shell.sh
19.6 其他 1 sed 's/a/A/;s/b/B/' my_shell.sh
1 sed -e 's/a/A' -e 's/b/B' my_shell.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script data-compress=strip> function h(obj){ obj.style.behavior='url(#default#homepage)' ; var a = obj.setHomePage('//www.baidu.com/' ); } </script> <script> _manCard = { asynJs : [], asynLoad : function (id ){ _manCard.asynJs.push(id ); } }; window._sp_async = 1; </script>
思路:
[^<>]
:匹配任意一个非 <> 的字符。[^<>]*
:确保匹配到 <> 中的一串字符。<[^<>]*>
:强制要求所匹配到的字符串两端需要有 <>。20.awk sed:处理行数据
awk:处理列数据
20.1 命令格式 awk 参数 ‘脚本语句’ 待操作文件 awk 参数 -f ‘脚本语句’ 待操作文件 20.2 命令参数 参数 含义 -F: 指定列的分隔符为 :
。冒号可以换成其他符号。
在 /etc/passwd 中,以 :
分割每一列。
1 awk -F: '{print $1}' /etc/passwd
20.3 脚本格式 1 2 /pattern/{action} condition{action}
**例:**有以下三行数据,筛选大于等于 50 的行。
1 2 3 product 30 product 76 product 55
1 2 awk '$2<50 {print $0} $2>=50 {print $0 " reorder"}' file awk '$2<50 {print $0} $2>=50 {print("%s reorder", $0)}' file
20.4 使用变量 **例:**统计文本的空行数。
1 awk '/^ *$/ {count=count+1} END {print count}' file
/^ *$/
:匹配空格开头,空格重复零次或多次,并以此结尾。{count=count+1}
:使用 count 进行计数。END
:匹配结束进行打印。**例:**统计 id 在 [2600, 2700) 的行数。
1 ps aux | awk '$2>=2600 && $2<2700 {count=count+1} END {print count}'
补充:
END 是在匹配最后进行操作。 BEGIN 是在匹配之前进行操作。 在匹配之前,对文本进行拆分。(以:
为分割进行拆分)
FS 是内建变量,实际上是宏。表示分隔符,缺省是来纳许的空格和 tab。
1 awk 'BEGIN {FS=":"} {print $1}' /etc/passwd
21.C语言使用正则 编译正则表达式 regcomp() 匹配正则表达式 regexec() 释放正则表达式 regfree()