1. chmod a+x myshell.sh
./myshell.sh
2. . myshell.sh
3. source myshell.sh
4. /bin/bash myshell.sh
注意
:对于如下案例shell脚本
#!/bin/bash
cd ..
ls
shell脚本只有一种数据类型,就是字符串
printenv命令
可以显示当前shell进程的环境变量。set命令
可以显示当前shell进程中定义的所有变量(包括本地变量和环境变量)和函数root@ssz:/home/ssz# VARNAME=value
注意等号两边都不能有空格,否则会被shell解释为命令和命令行参数
export命令
可以将本地变量导出为环境变量,定义和导出环境变量通常可以一步完成root@ssz:/home/ssz# export VARNAME=value
也可以分两步完成
root@ssz:/home/ssz# VARNAME=value
root@ssz:/home/ssz# export VARNAME
unset命令
可以删除已定义的环境变量或本地变量root@ssz:/home/ssz# unset VARNAME
* 匹配0个或任意个字符
root@ssz:/home/ssz# ls *.sh
sample.sh
? 匹配一个任意字符
root@ssz:/home/ssz# ls myshel?.sh
sample.sh
[若干字符] 匹配方括号中任意一个字符的一次出现
root@ssz:/home/ssz# ls myshell.s[hijk]
sample.sh
root@ssz:/home/ssz# DATE=`date`
root@ssz:/home/ssz# echo $DATE
Fri Nov 20 14:29:09 CST 2020
$()
表示root@ssz:/home/ssz# DATE=$(date)
Fri Nov 20 14:29:09 CST 2020
root@ssz:/home/ssz# VAR=45
root@ssz:/home/ssz# echo $(($VAR+3)) 等价于 $((VAR+3))、$[VAR+3]、$[$VAR+3]
48
$(()) 中只能用±*/和()运算符,并且只能做整数运算。
$[base#n],其中base表示进制,n按照base进制解释,后面再有运算数,按十进制解释
root@ssz:/home/ssz# echo $[2#10+11] 这里2#10表示2进制的10
13
root@ssz:/home/ssz# echo $[8#10+11]
19
root@ssz:/home/ssz# echo $[16#10+11]
27
root@ssz_webserver:/home/ssz# echo $SHELL
/bin/bash
root@ssz_webserver:/home/ssz# echo \$SHELL
$SHELL
root@ssz_webserver:/home/ssz# echo \\
\
root@ssz_webserver:/home/ssz# touch \$\ \$
root@ssz_webserver:/home/ssz# touch -hello
touch: invalid option -- 'e'
Try 'touch --help' for more information.
root@ssz_webserver:/home/ssz# touch \-hello
touch: invalid option -- 'e'
Try 'touch --help' for more information.
root@ssz_webserver:/home/ssz# touch ./-hello
root@ssz_webserver:/home/ssz# touch -- -hello
\还有一种用法,在\后敲回车表示续行
,shell并不会立刻执行命令,而是把光标移到下一行,给出一个续行提示符>,等待用户继续输入,最后把所有的续行接到一起当作一个命令执行。例如:root@ssz_webserver:/home/ssz# ls \
> -l
total 4
-rwxrwxrwx 1 root root 24 Nov 19 21:27 sample.sh
字符串的界定符
,而不是字符的界定符。单引号用于保持引号内所有字符的字面值
,即使引号内的 \ 和回车也不例外,但是字符串中不能出现单引号,如果引号没有配对就输入回车,shell会给出续行提示符,要求用户把引号配对上。例如:root@ssz_webserver:/home/ssz# echo '$SHELL'
$SHELL
root@ssz_webserver:/home/ssz# echo 'ABCD
> EFG'
ABCD
EFG
root@ssz_webserver:/home/ssz# DATE=$(date)
root@ssz_webserver:/home/ssz# echo "$DATE"
Fri Nov 20 16:05:46 CST 2020
root@ssz_webserver:/home/ssz# echo '$DATE'
$DATE
root@ssz_webserver:/home/ssz# VAR=200
root@ssz_webserver:/home/ssz# echo $VAR
200
root@ssz_webserver:/home/ssz# echo '$VAR'
$VAR
root@ssz_webserver:/home/ssz# echo "$VAR"
200
test命令
或者 [ 命令
可以测试一个条件是否成立,如果测试结果为真,则该命令的exit status为0,如果测试结果为假,则命令的exit status为1(注意,与C语言的逻辑正好相反)。例如测试两个数的大小关系:root@ssz_webserver:/home/ssz# var=2
root@ssz_webserver:/home/ssz# echo $var
2
root@ssz_webserver:/home/ssz# test $var -gt 1
root@ssz_webserver:/home/ssz# echo $? 查看上一条命令的执行结果
0
root@ssz_webserver:/home/ssz# test $var -gt 3
root@ssz_webserver:/home/ssz# echo $?
1
root@ssz_webserver:/home/ssz# [ $var -gt 3 ]
root@ssz_webserver:/home/ssz# echo $?
1
[ -d DIR ] # 如果DIR存在并且是一个目录则为真
[ -f DILE ] # 如果FILE存在且是一个普通文件则为真
[ -z STRING ] # 如果STRING的长度为0则为真
[ -n STRING ] # 如果STRIGN的长度非0则为真
[ STRIGN1 = STRING2 ] # 如果两个字符串相同则为真
[ STRIGN1 != STRING2 ] # 如果两个字符串不同则为真
[ ARG1 OP ARG2 ] # ARG1和ARG2应该是整数或者取值为整数的变量,OP是 -eq(等于) -ne(不等于)
# -lt(小于) -le(小于等于) -qt(大于) -qe(大于等于)之中的一个
[ ! EXPR ] # EXPR可以是上表中任意一种测试条件,!表示“逻辑非”
[ EXPR1 -a EXPR2 ] # EXPR1和EXPR2可以是上表中任意一种测试条件,-a表示“逻辑与”
[ EXPR1 -o EXPR2 ] # EXPR1和EXPR2可以是上表中任意一种测试条件,-o表示“逻辑或”
root@ssz_webserver:/home/ssz# VAR=abc
root@ssz_webserver:/home/ssz# [ -d shell_file -a $VAR = 'abc' ]
root@ssz_webserver:/home/ssz# echo $?
0
空字符串
,会造成测试条件的语法错误(展开为[ -d shell_file -a = ‘abc’ ]),作为一种好的shell编程习惯,应该总是把变量取值放在双引号之中(展开为[ -d shell_file -a “” = ‘abc’ ])root@ssz_webserver:/home/ssz# unset var
root@ssz_webserver:/home/ssz# [ -d Desktop -a $var = 'abc' ]
-bash: [: too many arguments
root@ssz_webserver:/home/ssz# [ -d Desktop -a "$var" = 'abc' ]
root@ssz_webserver:/home/ssz# echo $?
1
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
#! /bin/bash
if [ -f sample.sh ]
then
echo 'sample.sh is a file'
else
echo 'sample.sh is NOT a file'
if:
then
echo 'always true'
fi
空命令
,该命令不做任何事,但 exit status 总是真。此外,也可以执行 /bin/true或者 /bin/false得到真或假的 exit status。在看一个例子:#! /bin/bash
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]; then
echo "Good morning!"
elif [ "$YES_OR_NO" = "no" ]; then
echo "Good afternoon!"
else
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
return ;
fi
test "$(whoami)" != 'root' && (echo you are using a non-privileged account;)
test "$VAR" -gt 1 -a "$VAR" -lt 3
test "$VAR" -gt 1 && test "$VAR" -lt 3
每个匹配分支可以有若干条命令,末尾必须以;;结束
,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用 break 跳出。#! /bin/bash
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
echo "Good morning!";;
[nN]*)
echo "Good afternoon!";;
*)
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
return 1;;
esac
#! /bin/bash
for FRUIT in apple banana pear; do
echo "I like $FRUIT"
done
#! /bin/bash
for TEST in `ls`; do
ls -l $TEST
done
for FILENAME in chap?; do mv $FILENAME $FILENAME~; done
for FILENAME in `ls chap?`; do mv $FILENAME $FILENAME~; done
#! /bin/bash
echo "Enter password:"
read PASSWORD
while [ "$PASSWORD" != "secret" ]; do
echo "Sorry, password error, please try again"
read PASSWORD
done
#! /bin/bash
COUNT=1
while [ "$COUNT" -lt 10 ]; do
echo "Here we go again"
COUNT=$(($CONUT+1))
done
$0 // 相当于 C 语言 main 函数的 argv[0]
$1、$2... // 这些称为位置参数,相当于 C 语言 main 函数的argv[1]、argv[2]...
$# // 相当于 C 语言 main 函数的 argc - 1,注意这里的 # 后面不表示注释
$@ // 表示参数列表"$1" "$2"...,例如可以用在 for 循环中的 in 后面
$* // 表示参数列表"$1" "$2"...,例如可以用在 for 循环中的 in 后面
$? // 上一条命令的exut status
$$ // 当前进程号
#! /bin/bash
echo "The program $0 is now running"
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The third parameter is $3"
echo "The parameter list is $@"
shift
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The third parameter is $3"
echo "The parameter list is $@"
root@ssz_webserver:/home/ssz# . ./argv.sh aa bb cc
The program ./argv.sh is now running
The first parameter is aa
The second parameter is bb
The third parameter is cc
The parameter list is aa bb cc
The first parameter is bb
The second parameter is cc
The third parameter is
The parameter list is bb cc
echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显得内容后面跟一个回车换行
echo "hello\n\n" // 输出字符串 "hello\n\n"
echo -e "hello\n\n" // 输出字符串 "hello" 和两个空行
echo "hello" // 输出字符串 "hello",终端换行
echo -n "hello" // 输出字符串 "hello",终端不换行
cat myfile | more // 按页的形式读取文件
ls -l | grep "myfile"
df -k | awk '{print $1}' | grep -v "文件系统"
df -k 查看磁盘空间,找到第一列(awk '{print $1}'),去除“文件系统”,并输出
tee
命令把结果输出到标准输出,同时输出到另一个相应的副本文件(标准输出一份,文件副本一份)df -k | awk '{print $1}' | grep -v "文件系统" | tee a.txt
df -k | awk '{print $1}' | grep -v "文件系统" | tee -a a.txt
cmd > file
把标准输出重定向到新文件中root@ssz_webserver:/home/ssz# date > out
root@ssz_webserver:/home/ssz# cat out
Thu Dec 3 17:38:25 CST 2020
cmd >> file
把标准输出追加到文件中root@ssz_webserver:/home/ssz# ls | grep sample.sh >> out
root@ssz_webserver:/home/ssz# cat out
Thu Dec 3 17:38:25 CST 2020
sample.sh
cmd > file 2>&1
标准输出和标准出错都重定向到新文件中root@ssz_webserver:/home/ssz# rm as.txt > errfile 2>&1
root@ssz_webserver:/home/ssz# cat errfile
rm: cannot remove 'as.txt': No such file or directory
cmd < file1 > file2
从 file1 中读取到 file2root@ssz_webserver:/home/ssz# cat < out > out1
root@ssz_webserver:/home/ssz# cat out1
Thu Dec 3 17:41:46 CST 2020
sample.sh
cmd < &fd
将文件描述符 fd 作为标准输入
cmd > &fd
将文件描述符 fd 作为标准输出
cmd < &-
关闭标准输入
函数定义
中没有返回值也没有参数列表。例如:#! /bin/bash
foo(){
echo "Function foo is called"
}
echo "-=start=-"
foo
echo "-=end=-"
#! /bin/bash
foo(){
echo "Function foo is called"
for PARAM in $@; do
echo "$PARAM"
done
}
foo1(){
echo $1
echo $2
echo $3
}
echo "-=start=-"
foo $@
foo1 $1 $2 $3
echo "-=end=-"
一次创建多个目录
,各目录通过命令行参数传入,脚本逐个测试各目录是否存在,如果目录不存在,首先打印信息然后试着创建该目录。#! /bin/bash
is_directory() {
DIR_NAME = $1
if [ ! -d $DIR_NAME ]; then
return 1
else
return 0
fi
}
for DIT in "$@"; do
if is_directory "$DIR"
then :
else
echo "$DIR doesn't exist. Creating it now..."
mkdir $DIR > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Can't create directory $DIR"
exit 1
fi
fi
done
shell 提供了一些用于调试脚本的选项,如:
-n
读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。-v
一边执行脚本,一边将执行过的脚本命令打印到标准错误输出-x
提供跟踪执行信息,将执行的每一条命令和结果一次打印出来这些选项有三种常见的使用方法
在命令行提供参数。如:
$ sh -x ./test.sh
在脚本开头提供参数。如:
#! /bin/bash -x
在脚本中用 set 命令启用或者禁用参数
#! /bin/bash
if [ -z "$1" ]; then
set -x
echo "ERROR: Insufficient Args."
exit 1
set +x
fi
字符 | 含义 | 举例 |
---|---|---|
. | 匹配任意一个字符 | abc. 可以匹配abcd、abc9等 |
[ ] | 匹配括号中的任意一个字符 | [abc]d可以匹配ad、bd或cd |
- | 在[ ]括号内表示字符范围 | [0-9a-fA-F]可以匹配一位16进制数 |
^ | 位于[ ]内的开头,匹配除括号中的字符之外的任意一个字符 | [^xy]匹配除xy之外的任一字符,如a1、b1 |
[[:xxx:]] | grep工具预定义的一些命名字符类 | [[:alpha:]]匹配一个字母,[[:digit:]]匹配一个数字 |
字符 | 含义 | 举例 |
---|---|---|
? | 紧跟在它前面的单元应匹配一次或零次 | [0-9]?.[0-9]匹配0.0、2.3、.5等,由于 . 在正则表达式中是一个 特殊字符,所以需要用转义字符转义一下,取字面值 |
+ | 紧跟在它前面的单元应匹配一次或多次 | [a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+匹配 |
* | 紧跟在它前面的单元应匹配零次或多次 | [0-9][0-9]*匹配至少一位数字, 等价于[0-9]+,[a-zA-Z_]+[a-zA-Z_0-9]*匹配C语言的标识符 |
{N} | 紧跟在它前面的单元应精确匹配N次 | [0-9][0-9]{2}匹配从100到999的整数 |
{N, } | 紧跟在它前面的单元应匹配至少N次 | [0-9][0-9]{2, }匹配三位以上(含三位)的整数 |
{, N} | 紧跟在它前面的单元应匹配最多N次 | [0-9]{,1}相当于[0-9]? |
{N, M} | 紧跟在它前面的单元应匹配N到M次 | [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}匹配IP地址 |
包含某一模式
的行,而不是完全匹配某一模式
的行字符 | 含义 | 举例 | |
---|---|---|---|
^ | 匹配行首的位置 | ^Content 匹配位于一行开头的 Content | |
$ | 匹配行末的位置 | ;^匹配位于一行结尾的 ; 号,^$匹配空行 | |
\< | 匹配单词开头的位置 | \ | |
\> | 匹配单词结尾的位置 | p\>匹配 leap …,但不匹配 parent、sleepy | |
\b | 匹配单词开头或结尾的位置 | \bat\b 匹配 … at …,但不匹配 cat、atexit、batch | |
\B | 匹配非单词开头或结尾的位置 | \Bat\B 匹配 battery,但不匹配 … attend、hat … |
192.168.1.1
1234.234.04.5678
如果用 1{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ 查找,就可以把 1234.234.04.5678 这一行给过滤掉
也可以用 ^([0-9]{1,3}\.){3}[0-9]{1,3}$
来查找 IP 地址
字符 | 含义 | 举例 |
---|---|---|
\ | 转义字符,普通字符和特殊字符之间相互转义 | 普通字符 < 写成 \<表示单词开头的位置,特殊字符 . 写成 \. |
() | 将正则表达式一部分括起来组成一个单元,可以对整个单元使用数量限定符 | ^([0-9]{1,3}\.){3}[0-9]{1,3}$ 匹配 IP 地址 |
| | 连接两个子表达式,表示或的关系 | n(o|either) 匹配 no 或 neither |
grep [options]
主要参数: grep --help 可查看
-c:只输出匹配行的计数
-i:不区分大小写
-h:查询多文件时不显示文件名
-l:查询多文件时只输出包含匹配字符的文件名
-n:显示匹配行及行号
-w:只匹配整个单词,而不是字符串的一部分
-s:不显示不存在或无匹配文本的错误信息
-v:显示不包含匹配文本的所有行
--color=auto:可以将找到的关键词部分加上颜色的显示
\:忽略正则表达式中特殊字符的原有含义
^:匹配正则表达式的开始行
$:匹配正则表达式的结尾行
\<:从匹配正则表达式的行开始
\>:从匹配正则表达式的行结束
[ ]:单个字符,如[a]即a符合要求
[ - ]:范围,如[a-z]即a、b、c...z都满足
.:所有的单个字符
*:所有字符,长度可以为0
grep 'test' d* 显示所有以 d 开头的文件中包含test的行
grep 'test' aa bb cc 显示在aa、bb、cc文件中匹配test的行
grep '[a-z]\{5\}' aa 显示所有包含每个字符串至少有5个连续小写字符的字符串的行
grep 'w\(es\)t.*\1' aa 如果west被匹配到,则es就被存储到内存中,并被标记为1,然后搜索任意个字符
(.*),这些字符后面紧跟着另一个es(\1),找到就显示该行。如果用egrep或
grep -E 就不用"\"号进行转义,直接写成 'w(es)t.*\1' 就可以了
find pathname -options [-print -exec -ok ...]
find ./ -name “init”
find ./ -size +3M -size -7M
find ./ -type f
find ./ -maxdepth 2 -type d
find ./ -maxdepth 2 -type d -exec ls -ld {} \;
find -maxdepth 1 -type d -ok rm -rf {} \;
find -maxdepth 1 -type f | xargs ls -ld
find -maxdepth 1 -type f -print0 | xargs -0 ls -ld
find ./ -name "syslog*" -mtime +5 -exec ls -l {} \;
sed option 'script' file1 file2 ... sed 参数 `脚本(/pattern/action)` 待处理文件
sed option -f scriptfile file1 file2 ... sed 参数 -f `脚本文件` 待处理文件
--version 显示 sed 版本
--help 显示帮助文档
-n, --quiet, --silent 静默输出,默认情况下,sed 程序在所有的脚本指令执行完毕后,
将自动打印模式空间中的内容,这些选项可以屏蔽自动打印
-e script 允许多个脚本指令被执行
-f script-file,
--file=script-file 从文件中读取脚本指令,对编写自动脚本程序来说很棒
-i,--in-place 直接修改源文件,经过脚本指令处理后的内容将被输出至源文件
-l N, --line-length=N 该选项指定l指令可以输出的行长度,l指令用于输出非打印字符
--posix 禁用GNU sed扩展功能
-r, --regexp-extended 在脚本指令中使用扩展正则表达式
-s. --separate 默认情况下,sed将把命令行指定的多个文件名作为一个长的连续的输入流
而GNU sed则允许把他们当作单独的文件,这样如正则表达式则不进行跨文件匹配
a, append 追加
i, insert 插入
d, delete 删除
s, substitution 替换
sed '2,5d' testfile
删除2-5行
/pattern/action
/pattern/p 打印匹配pattern的行
/pattern/d 删除匹配pattern的行
/pattern/s/pattern1/pattern2/ 查找符合pattern的行,将该行第一个匹配pattern1的字符串替换为pattern2
/pattern/s/pattern1/pattern2/g 查找符合pattern的行,将该行所有匹配pattern1的字符串替换为pattern2
123
abc
456
root@ssz_webserver:/home/ssz# sed '/abc/p' testfile
123
abc
abc
456
root@ssz_webserver:/home/ssz# sed -n '/abc/p' testfile
abc
root@ssz_webserver:/home/ssz# sed '/abc/d' testfile
123
456
注意
:sed 命令不会修改源文件,删除命令指标是某些行不打印输出,而不是从源文件中删去
使用查找替换命令时,可以把匹配 pattern1 的字符串复制到 pattern2 中,比如:
root@ssz_webserver:/home/ssz# sed 's/bc/-&-/' testfile
123
a-bc-
456
pattern2 中的 & 表示源文件的当前行中与 pattern1 相匹配的字符串
root@ssz_webserver:/home/ssz# sed 's/\([0-9]\)\([0-9]\)/-\1-~\2~/' testfile
-1-~2~3
abc
-4-~5~6
将第一个数前后加上- -,在第二个数前后加上 ~ ~,括号前面的 \ 是转义括号的
sed -r 's/([0-9])([0-9])/-\1-~\2~/' out.sh
sed 's/yes/no/;s/static/dhcp/' testfile
注:使用分号;隔开指令
sed -e 's/yes/no/ -e s/static/dhcp/' testfile
注:使用 -e 选项
<html><head><title>Hello Worldtitle>head>
<body>Welcome to the world of regexp!body>html>
Hello World
Welcome to the world of regexp!
sed 's/<.*>//g' testfile
<html><head><title>Hello Worldtitle>head>
sed 's/<.[a-z]*>//g' testfile
或
sed 's/<[^>]*>//g' testfile
awk 缺省的行分隔符是换行,缺省的列分隔符是连续的空格和Tab
,但是行分隔符和列分隔符都可以自定义,比如 /etc/passwd 文件的每一行有若干个字段,字段之间以 : 分隔,就可以重定义 awk 的列分隔符为 : 并以列分隔符为单位处理这个文件。awk option 'script' file1 file2 ... awk 参数 `脚本(pattern{action}` 待处理文件
awk option -f scriptfile file1 file2 ... awk 参数 -f `脚本文件` 待处理文件
/pattern/{actions}
condition{actions}
pattern 是正则表达式,actions 是匹配成功后的一系列操作
。awk 程序一行一行读出待处理文件,如果某一行与 pattern 匹配,或者满足 condition 条件,则执行相应的 actions。如果一条 awk 命令只有 actions 部分,则 actions 作用于待处理文件的每一行
。比如文件 testfile 的内容表示某商店的库存量:ProductA 30
ProductB 76
ProductC 55
root@ssz_webserver:/home/ssz# awk '{print $2;}' testfile
30
76
55
awk '$2>75 {print $0} $2<75 {printf("%s %s\n", $0, "reorder");}' testfile
或
awk '$2>75 {print $0} $2<75 {printf "%s %s\n", $0, "reorder"}' testfile
或
awk '$2>75 {print $0} $2<75 {print $0 " reorder";}' testfile
ProductA 30 REORDER
ProductB 76
ProductC 55 REORDER
BEGIN 后面的 actions 在处理整个文件之前执行一次,END 后面的 actions 在整个文件处理完之后执行一次。
awk '/^ *$/ {x=x+1;} END {print x;}' testfile
awk 'BEGIN {FS=":"} {print $1;}' /etc/passwd
FILENAME 当前输入文件的文件名,该变量是只读的
NR 当前行的行号,该变量是只读的,R代表record
NF 当前行所拥有的列数,该变量是只读的,F代表field
OFS 输出格式的列分隔符,缺省是空格
FS 输入文件的列分隔符,缺省是连续的空格和Tab
ORS 输出格式的行分隔符,缺省是换行符
RS 输入文件的行分隔符,缺省是换行符
编译正则表达式 regcomp()
匹配正则表达式 regexec()
释放正则表达式 regfree()
int regcomp (regex_t *compiled, const char* pattern, int cflags)
regex_t 是一个结构体数据类型,用来存放编译后的正则表达式,它的成员 re_nsub 用来存储正则表达式中
的子正则表达式的个数,子正则表达式就是用圆括号括起来的部分表达式
pattern 是指向我们写好的正则表达式的指针
cflags 有如下4个值或者是它们或运算(|)后的值:
REG_EXTENDED 以功能更加强大的扩展正则表达式的方式进行匹配
REG_ICASE 匹配字母时忽略大小写
REG_NOSUB 不用存储匹配后的结果,只返回是否成功匹配,如果设置该标志位,那么在 regexec
nmatch = 0 和 pmatch = NULL,没有内嵌的子正则表达式
REG_NEWLINE 识别换行符,这样 '$' 就可以从行尾开始匹配,'^' 就可以从行的开头开始匹配
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
int regexec (const regex_t *compiled, const char *string,
size_t nmatch, regmatch_t matchptr[], int eflags)
compiled 是已经用 regcomp 函数编译好的正则表达式
string 是目标文本串
nmatch 是 regmatch_t 结构体数组的长度,若使用该参数,需设置非 REG_NOSUB
matchptr regmatch_t 类型的结构体数组,存放匹配文本串的位置信息
eflags 有两个值:
REG_NOTBOL 让特殊字符 ^ 无作用
REG_NOTEOL 让特殊字符 $ 无作用
void regfree (regex_t *compiled)
size_t regerror(int errcode, regex_t *compiled, char *buffer, size_t length)
errcode 是由 regcomp 和 regexec 函数返回的错误代码
compiled 是已用 regcomp 函数编译好的正则表达式,这个值可以为 NULL
buffer 指向用来存放错误信息的字符串的内存空间
length 指明 buffer 的长度,如果这个错误信息的长度大于这个值,则 errcode 函数会自动截断
超出的字符串,但他仍然会返回完整的字符串的长度。所以我们可以用如下的方法先得到错误
字符串的长度
#include
#include
#include
int main(int argc, char **argv) {
if(argc != 3) {
printf("Usage: %s RegexString Text\n", argv[0]);
return 1;
}
const char * pregexstr = argv[1]; // 正则表达式
const char * ptext = argv[2]; // 字符串
regex_t oregex; // 编译后的结构体
int nerrcode = 0;
char szerrmsg[1024] = {0}; // 保存错误信息的数组
size_t unerrmsglen = 0;
// 编译正则表达式,扩展正则
if((nerrcode = regcomp(&oregex, pregexstr, REG_EXTENDED|REG_NOSUB)) == 0) {
// 执行匹配,不保存匹配的返回值
if((nerrcode = regexec(&oregex, ptext, 0, NULL, 0)) == 0) {
printf("%s matches %s\n", ptext, pregexstr);
regfree(&oregex);
return 1;
}
}
// 正则编译错误,szerrmsg中保存错误描述
unerrmsglen = regerror(nerrcode, &oregex, szerrmsg, sizeof(szerrmsg));
// 若错误信息较长
unerrmsglen = unerrmsglen < sizeof(szerrmsg) ? unerrmsglen : sizeof(szerrmsg) - 1;
szerrmsg[unerrmsglen] = '\0';
printf("Regex error Msg: %s\n", szerrmsg);
regfree(&oregex);
return 0;
}
./a.out "https:\/\/www\..*\.com" "https://www.taobao.com"
./a.out "^[a-zA-Z0-9]+@[a-zA-Z0-9]+.[a-zA-Z0-9]+" "[email protected]"
./a.out "\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" "[email protected]"
注:\w 匹配一个字符,包含下划线
0-9 ↩︎