Linux Shell 脚本语法以及常用命令

目录

  • /etc/passwd
  • 确定当前shell
  • shell的内建命令
  • 执行脚本
  • 基本语法
    • 变量
    • 变量的分类
    • 删除变量unset
    • 文件名代换*/?/[]
    • 参数扩展{}
    • 命令代换``/$()
    • 算术代换 ( ( ) ) / (())/ (())/[]
    • 转移字符\
    • 单引号''
    • 双引号""
  • 脚本语法
    • 条件测试test/[
    • if分支结构
    • case分支结构
    • for循环
    • while循环
    • break/contine
    • 位置参数和特殊变量
    • 输入输出
    • 管道
    • tee
    • 文件重定向
    • 函数
    • Shell脚本的调试方法
    • 正则表达式
    • C语言中的正则匹配
  • 常用命令
    • sort命令
    • uniq
    • wc
    • grep
    • find
    • xargs
    • sed
    • awk
    • crontab

/etc/passwd

是一个多行多列的表格,列的分隔符号是冒号

root:x:0:0:root:/root:/bin/bash 
用户名:以前是密码,现在x是加密放到shadow:用户id:组id:
用户描述:home目录地址:用户登入之后执行的第一条命令
如果用户启动的第一条命令不是shell的话,那么执行完命令就会退出
mon:/usr/sbin:/usr/sbin/nologin,这个就不能登入。

确定当前shell

echo $SHELL  查看环境变量
看passwd文件用户的启动命令
修改当前用户的shell: chsh

shell的内建命令

  • 查看 man bash-builtins
  • shell的内建命令是不会启动进程的,而是就在当前shell的进程下去调用一个函数,执行后返回一个状态码,非内建的本质就是fork,然后exec启动一个进程。

执行脚本

#!/bin/bash
# 第一行表示解释器,表明使用的sh解释器
echo HelloWorld
cd .. #这里是子shell进程执行,子shell进程工作目录变化,父shell进程不会受影响
ls

执行脚本:

chmod a+x a.sh
./a.sh # 需要可执行权限
sh a.sh # 需要可读权限,不用可执行权限,在一些没办法调整脚本权限的情况下使用,和第一种方法类似
# 有一个弊端:要调用者关注当前脚本使用的解释器
source a.sh
. a.sh
# 上面的两种都是使用内建命令来执行的,不会创建子shell进程。就在当前shell进程执行。

基本语法

变量

shell变量通常由字母加下划线开头,由任意长度的字母、数组、下划线组成。
赋值一个变量: VARNAME=value注意等号两边不能由空格,否则会被shell解释成命令和命令行参数。
变量的使用,用$符号跟上变量名表示对某个变量取值,变量名可以加上花括号表示变量名的范围echo $VARNAME, echo ${VARNAME}_suffix。使用花括号来分离,不至于将VARNAME_suffix当作一个变量。

变量的分类

  • shell 内变量:在shell的解析环境中存在的变量,分为局部变量全局变量。shell不适用任何修饰符修饰的变量都是全局变量,不管在函数内部还是外部,生命周期从声明语句调用开始一直到shell结束。局部需要使用local修饰,只能声明在函数内部,生命周期从声明语句调用开始一直到函数结束。shell内变量只能在当前shell进程中使用,垮了进程就不能使用
#!/bin/bash
globalVar1="Hello" # 全局变量
function test() # 在sh解释器中没有function关键字,在bash中有,所以funcuton关键字可以省略掉
{
    globalVar2="world" # 全局变量
    local localVar1="itcast" # 局部变量
    echo ${localVar1} # itcast
}
test
echo ${globalVar1} ${globalVar2} ${localVar1} # 输出结果 Hello world 局部变量不存在
./subscript.sh # 不会将全局变量传递过去,他们是两个进程
source subscript.sh # 他们是同一个进程,可以在子脚本中使用该全局变量,会打印出Hello

# sub script
#!/bin/bash
echo "this is a sub script"
echo ${globalVar1}
echo "sub script end"
  • 环境变量:是操作系统自带的,每一个进程都会有。当启动一个子进程的时候,环境变量是从父进程拷贝到子进程的。可以用于进程间、脚本间传递参数。环境变量是单向传递的,子进程做任何环境变量的修改,都不会影响父进程
export varname=value # 将varname添加到环境变量
varname1=value
export varname1 # 将varname1添加到环境变量

#!/bin/bash
envVar="hello world"
export $envVar
# 调用子脚本
./subScript.sh # 输出 hello world
# 验证子进程中修改环境变量,父进程有没有改变
echo $envVar # 输出 hello world 子进程环境变量不会改变父进程的

# subScript.sh
#!/bin/bash
echo ${envVar}
export envVar="2222222222"

删除变量unset

不管是环境变量还是普通的shell变量,都可以使用unset关键字进行删除。

unset varname

文件名代换*/?/[]

文件名代换,也就是 Globbing 。使用匹配的字符,也就是通配符。代换发生在命令执行之前。代换的前提是文件存在,不存在代换不了。

  • ***** :匹配 0 个或多个任意字符 *.txt
  • ?:匹配任意一个字符 ?.txt
  • [若干字符]:匹配方括号中任意一个字符的一次出现, R[0-8]_[8-7]

参数扩展{}

# 下面的功能是一样的,都是创建1到4的txt文件 
touch 1.txt 2.txt 3.txt 4.txt
touch {1..4}.txt
touch {1,2,3,4}.txt

mkdir -p day{1..9}/0{1_code,2_doc,3_resource,4_node}
# 创建day1到9的文件夹,并且每个文件夹中都有01_code,02_doc,03_resource,04_node四个文件夹。

命令代换``/$()

使用"`"反引号括起来的也是一条命令,shell先执行该命令,然后将输出结果立即代换到当前命令中。执行某一条命令,将这个命令的标准输出的内容存储到某个变量中。

DATE=`date`
echo ${DATE}
DATE=$(date) # 和反引号的功能是一致的
# 获取当前脚本的所在目录
curPath=$(cd `dirname $0`;pwd)
# cd `dirname $0` 是进入脚本所在的目录

算术代换 ( ( ) ) / (())/ (())/[]

使用$(()),用于算术计算,(())中的shell变量取值将转换成整数,和同样含义的$[]等价。

VAR=45
echo $(($VAR+3)) 等价于 $((VAR+3)) 或者 echo $[VAR+3]或 $[$VAR+3]

他们只能使用+ - * /()运算符,并且只能做整数运算$[base#n],其中base表示进制, n按照base进制进行解释,后面再有运算数,按十进制解释。

echo $[8#10+11] #输出 19
echo $[16#10+11] #输出 27

转移字符\

\在shell中用作转移字符,用于去除紧跟其后的单个字符的特殊意义(回车除外),换句话说,紧跟其后的字符取字面值。

echo \$SHELL # 输出$SHELL
touch \$\ \$ # 创建$ $为名的文件

单引号’’

单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号。

echo '$SHELL' # 输出$SHELL
echo 'ABC\
DE' # 输出ABC\回车DE

双引号""

和单引号一致,但他防止通配符的扩展,允许变量的扩展。被双引号括住的内容,被视为单一字符串。

echo "$SHELL" # 输出 /bin/bash
var="a.txt b.txt"
touch $var # 创建两个文件 a.txt和b.txt
touch "$var" # 创建一个文件 名字为 a.txt b.txt

脚本语法

条件测试test/[

Shell 条件测试 test 用于在脚本中根据条件检查执行分支。test 命令通常以 [ ] 或者 [[ ]] 的形式出现。直接使用某条命令的返回状态表示真假,main函数返回0表示真,返回非0表示假。

  1. 检查文件是否存在:

    • [ -e 文件路径 ]:如果文件存在,则返回 true。
    • [ -f 文件路径 ]:如果文件存在且为普通文件,则返回 true。
    • [ -d 目录路径 ]:如果目录存在,则返回 true。
  2. 数值比较:

    • [ 数值1 -eq 数值2 ]:如果两个数值相等,则返回 true。
    • [ 数值1 -ne 数值2 ]:如果两个数值不相等,则返回 true。
    • [ 数值1 -lt 数值2 ]:如果数值1小于数值2,则返回 true。
    • [ 数值1 -le 数值2 ]:如果数值1小于或等于数值2,则返回 true。
    • [ 数值1 -gt 数值2 ]:如果数值1大于数值2,则返回 true。
    • [ 数值1 -ge 数值2 ]:如果数值1大于或等于数值2,则返回 true。
  3. 字符串比较:

    • [ 字符串1 = 字符串2 ]:如果两个字符串相等,则返回 true。
    • [ 字符串1 != 字符串2 ]:如果两个字符串不相等,则返回 true。
    • [ -z 字符串 ]:如果字符串为空,则返回 true。
    • [ -n 字符串 ]:如果字符串非空 nonzero,则返回 true。
  4. 逻辑判断:

    • [ 表达式1 -a 表达式2 ]:逻辑与 and,如果两个表达式都为 true,则返回 true。
    • [ 表达式1 -o 表达式2 ]:逻辑或 or,如果两个表达式中至少有一个为 true,则返回 true。
    • [ ! 表达式 ]:逻辑非,如果表达式为 false,则返回 true。

注意:[[ 支持更多的高级特性,比如模式匹配和正则表达式,推荐在使用条件测试时使用 [[ ]]。更详细的用法可以查看 Shell 的官方文档或者使用 man test 命令查看 test 命令的帮助文档。

echo $? # 打印上条命令的返回状态,0为真,1为假
( EXPRESSION ): 测试该表达式是否为真
! EXPRESSION:取反,逻辑非
EXPRESSION1 -a EXPRESSION2:and 逻辑与
EXPRESSION1 -o EXPRESSION2:or 逻辑或
-n STRING:nonzero, 判断字符串长度为非零
-z STRING:zero, 判断字符串长度为零
STRING1 = STRING2:判断字符串相等
STRING1 != STRING2:判断字符串不相等
INTEGER1 -eq INTEGER2:两个数值相等
INTEGER1 -ge INTEGER2:判断数值1大于等于数值2
INTEGER1 -gt INTEGER2:判断数值1大于数值2
INTEGER1 -le INTEGER2:判断数值1小于等于数值2
INTEGER1 -lt INTEGER2:判断数值1小于数值2
INTEGER1 -ne INTEGER2:两数值不相等
FILE1 -ef FILE2:FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2:判断文件1比文件2新(最后修改时间)
FILE1 -ot FILE2:判断文件1比文件2旧(最后修改时间)
-b FILE:block,判断文件为块设备
-c FILE:character,判断文件为字符设备
-d FILE:directory,判断是否为目录
-e FILE:exist 判断文件是否存在
-f FILE:判断文件为普通文件
-g FILE:是否拥有有效组ID
-G FILE:FILE exists and is owned by the effective group ID
-h FILE:判断是否为一个符号链接 link
-k FILE:判断粘着位是否设置
-L FILE:判断是否为一个符号链接 link
-N FILE:判断文件最后一次读取后文件的长度是否大于0
-O FILE:判断文件是否有有效的用户ID
-p FILE:是不是命令管道
-r FILE:是否有读权限
-s FILE:判断文件大小是否大于0
-S FILE:判断文件是不是Socket文件
-t FD : 判断文件描述符是否被终端打开
-u FILE:判断文件用户ID是否被设置
-w FILE:判断文件是否有写权限
-x FILE:判断文件是否有可执行权限

if分支结构

# 语法结构
if 命令/条件测试
then
	xxxxxx
elif 命令2/条件测试2 ; then # 如果then和if写在同一行需要加分号
	xxxxxx
else # else不用加then
	xxxxxx
fi # 将if倒着写

#!/bin/sh
if [ -f /bin/bash ]; then
    echo "/bin/bash is a file"
else
    echo "/bin/bash is not a file"
fi

# ":",是一个空命令,总是为真
if : # 等价于 if true
then
	echo "always true"
fi

echo "Is it morning? Please answer yes or no."
read YES_OR_NO # 在终端输入一行字符串,保存到变量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} is not a recognized, Enter yes or no."
fi

## 在shell中 && 和 || 用于连接两条命令,分别代表逻辑与和逻辑或
## 而-a 和 -o是仅用于条件测试中来连接两条测试命令的。下面是等价的
VAR=2
test "$VAR" -gt 1 -a "$VAR" -lt 3
test "$VAR" -gt 1 && test "$VAR" -lt 3
VAR=2
if [ "$VAR" -gt 1 ] && [ "$VAR" -lt 3 ]; then
    echo "true"
fi
if [ "$VAR" -gt 1 -a "$VAR" -lt 3 ]; then
    echo "true"
fi
# 短路特性
make && make install # 如果make执行失败,那么不执行make install
echo xxx || exit -1 # 如果xxx执行失败,那么执行exit -1命令

case分支结构

case … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

# 语法结构
case 表达式 in
val1|pattern1)
	xxxx
	;;
val2|pattern2)
	xxxx
	;;
*)
	xxxx
	;;
esac # 将case倒着些


#!/bin/bash
echo "Is it morning? Please answer yes or no"
read ANSWER
case "$ANSWER" in
yes|Y|y|YES|Yes)
    echo "Good Morning";;
[nN][Oo]) # 代表组合no|nO|No|NO
    echo "Good Afternoon";;
*)
    echo "Sorry, no recognized";;
esac

for循环

# 语法结构
for 临时变量 in 列表; do
	xxxxx
done

for fruit in apple banana pear; do # do 可以单独写一行,那么就可以不需要分号了
	echo "$fruit"
done
sum=0
for i in {1..100} # 循环加1-100 使用sh解释器没有花括号,使用 $(seq 1 100) 替换
do
	sum=$[$sum+i]
done
echo $sum
# 遍历当前目录
for i in $(ls)
do
	if [ -f "$i" ]; then
		echo "$i is a file"
	elif [ -d "$i" ]; then
		echo "$i is a dir"
	else
		echo "$i no recognized"
	fi
done

while循环

while 命令/条件测试 ;do # do 可以单独写一行,那么就可以不需要分号了
	xxxxx
done

sum=0
count=1
while [ $count -lt 100 ]; do
	sum=$(($sum+$count))
	count=$(($count+1))
done
echo "sum = $sum"

break/contine

他们的意义和c/c++的是一致的。

break[n] # 跳出几层循环
continue # 跳过本次循环,但不会跳出循环

#!/bin/sh
echo "input passwd"
read try
count=1
while [ "$try" != "passwd" ]; do
	if [ $count -gt 5 ]; then # 等价于在while条件测试中添加 -a $count -gt 5
		break
	fi
    echo "sorry! try against"
    count=$(($count+1))
    read try
done

位置参数和特殊变量

有很多特殊变量是被shell自动赋值的

$0  # 相当于C语言main函数的argv[0]
$1$2... # 相当于C语言main函数的argv[1]、argv[2]....
$# # 相当于C语言main函数的argc-1
$@ # 表示参数列表 "$1" "$2" "$3" ... 可以用在for循环中的in的后面
$* # 表示参数列表 "$1" "$2" "$3" ... 和$@一致
$? # 表示上一条命令的Exit Status
$$ # 当前进程号

位置参数可以使用shift命令左移。比如shift 3表示原来$4现在变成了$1,原来的$5现在变成了$2等等。原来的$1$2$3被丢弃,$0不移动。不带参数的shift命令相当于shift 1。也就是说shift语句和shift 1是等价的。位置参数最多只支持$0-$9,所以要后面的参数,就必须shift

# ./shift.sh one two three four
#!/bin/sh
echo $$  # 169273
echo $@  # one two three four
echo $#  # 4
echo $?  # 0
echo $0  # ./shift.sh
echo $1  # one
echo $2  # two
echo $3	 # three
shift 1
echo $3	 # four

输入输出

echo [option] string
-e 解析转义字符
-n 不回车,默认情况下echo回显内容后面跟一个回车换行
echo -e "hello\t123"
echo -n "hello"
printf "%d\t%s\r\n" 123 "hello"  # 123	hello

管道

通过|命令把一个命令的输出传递给另一个命令做输入。原理为将前面的进程的标准输出重定向到后面进程的标准输入。

cat filename | grep "hello"
cat myfile | more

tee

tee命令把结果输出到标准输出,另一个副本输出到相应的文件。

df -k | awk '{print $1}' | grep -v "文件系统" | tee a.txt
df -k | awk '{print $1}' | grep -v "文件系统" | tee -a a.txt # -a表示追加到a.txt后

文件重定向

cmd > file  # 把标准输出重定向到新文件中
cmd >> file # 最加到文件中
cmd > file 2>&1 # 标准输出重定向到file文件
			# 文件描述符2标准错误输出重定向到文件描述1标准输出中。
cmd < file1 > file2 # 将标准输入重定向到file1,也就是file1作为标准输入 标准输出重定向到file2
cmd < &fd # 把文件描述符fd作为标准输入
cmd > &fd # 把文件描述符fd作为标准输出
cmd < &-  # 关闭标准输入

函数

function 函数名() # 在sh解释器中没有function关键字,必须省略
{
	local var=value # 局部变量
	xxxx
	retrun 0 # 只能返回整数,作为退出状态,不能返回其他,
			# 也可以不写retrun返回,那么就以函数最后一条指令返回状态作为整个函数的退出状态
}
# 在bash解释器中,function和小括号可以省略,但是必须要有一个存在,不然无法识别为函数。
# 调用函数 函数名 arg1 arg2 arg3 ... 

testfunction()
{
	echo "aaaa"
	return 2 # 那么此时echo $? 就是2
}
# 使用命令代换的方式获取函数的输出值aaaa
var=$((testfunction))

# 函数传参
# 在函数的内部同样是通过 $0 $1 $2 $3 .. 来获取参数
# 函数支持递归,遍历整个目录
visit()
{
	local dir="$1" # 获取当前目录
	for f in `ls $dir` 
	do
		if [ -f "$dir/$f" ]; then
			echo "$dir/$f is a file"
		elif [ -d "$dir/$f" ]; then
			echo "$dir/$f is a dir"
			visit "$dir/$f"  # 递归进行遍历
		else
			echo "$dir/$f is not regcognized"
		fi
	done
}

Shell脚本的调试方法

-n 读一遍脚本的命令但不执行,用于检查脚本中的语法错误
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来

sh -n script.sh # 在命令行提供参数
# 在脚本开头提供参数
#!/bin/sh -x 
# 在脚本中使用set命令启动或者禁用参数
set -x # 打开调试
set +x # 关闭调试

正则表达式

一种匹配字符串的方法,通过一些特殊符号,实现快速查找、删除、替换某个特定字符串。是由普通字符与元字符组成的文字模式。

  • 基础正则表达式
元字符 作用
\ 转义字符,用于取消特殊字符的含义,例如:\!\n
^ 匹配字符串开始的位置,如:^hello,匹配以hello开头的行
$ 匹配字符串结束的位置,如:hello$,匹配以hello结尾的行
. 匹配除了\n换行之外的任意一个字符
* 匹配前面子表达式0次或者多次
[list] 匹配list列表的一个字符,[0-9]匹配0-9的任意一个数字
[^list] 匹配不在list列表的一个字符,[^0-9]匹配任意非数字
{n} 匹配前面的表达式n次,[0-9]{2}匹配2位数字
{n,} 匹配前面的表达式不少于n次,[0-9]\{2,}匹配2位以上的数字
{n,m} 匹配前面的表达式n到m次,[a-z]{2,3}匹配2到3位的小写字母
  • 扩展正则表达式(在基础上面将+?等作为特殊字符)
元字符 作用
+ 匹配前面子表达式一次以上,go+d,将匹配至少一个o
? 匹配前面子表达式0次或者一次,go?d,匹配gd或者god
() 将括号中的作为一个整体,g(oo)+d,匹配整体一次以上,例如goodgooood
` `
  • perl正则表达式

在扩展正则表达式的上面添加了一些特殊字符,是最常用的。

\d	匹配一个数字的字符,和 [0-9] 语法一样
\d+	匹配多个数字字符串,和 [0-9]+ 语法一样
\D	非数字,其他同 \d
\D+	非数字,其他同 \d+
\w	英文字母或数字的字符串,和 [a-zA-Z0-9_] 语法一样
\w+	和 [a-zA-Z0-9_]+ 语法一样
\W	非英文字母或数字的字符串,和 [^a-zA-Z0-9_] 语法一样
\W+	和 [^a-zA-Z0-9_]+ 语法一样
\s	空白符号,和 [\n\t\r\f] 语法一样
\s+	和 [\n\t\r\f]+ 一样
\S	非空白符号,和 [^\n\t\r\f] 语法一样
\S+	和 [^\n\t\r\f]+ 语法一样
\b	匹配以英文字母,数字为边界的字符串
\B	匹配不以英文字母,数字为边界的字符串
  • grep查找命令选项
命令 作用
-n 显示行号
-i 不区分大小写
-v 反向查找
以S开头的字符串:^S
以数字结尾的字符串:[0-9]$
匹配空字符串:^$
字符串只包含三个数字:^\d\d\d$  ^[0-9]{3}$ ^\d{3}$
字符串只有3到5个字母:^[a-zA-Z]{3,5}$
匹配不是a-z的任意字符串:[^a-z]
匹配有字符串0-1个数字、字母或者下划线:^[0-9a-zA-Z_]?$ ^\w?$
字符串有一个或多个空白符号(\t\n\r等): ^\s+$ ^\s{1,}$
字符串有0个或若干个任意字符(除了\n):^.{,}$  ^.*$
匹配0或任意多组ABC:^(ABC)*$
字符串要么是ABC,要么是123:^ABC$|^123$ ^(ABC|123)$
字符串只有一个点号:^[.]$
匹配十进制3位数字:^[1-9][0-9]{2}$
匹配0-999的数字:^([0-9]|[1-9][0-9]{1,2})$
匹配0-255的数字:^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$

C语言中的正则匹配

#include
int regcomp(regex_t *preg, const char *pattern, int cflags)
    编译正则表达式
    preg:指向regex_t结构体的指针,用于存储编译后的正则表达式。
	pattern:要编译的正则表达式字符串。
	cflags:编译选项,可以使用以下常量按位或操作组合:
        REG_EXTENDED:支持扩展正则表达式语法。
        REG_ICASE:忽略大小写。
        REG_NOSUB:不存储匹配的子字符串。
        REG_NEWLINE:将'.'视为匹配任何字符,包括换行符。
int regexec(const regex_t *preg, const char *string, size_t nmatch, 
            regmatch_t pmatch[], int eflags)
   	进行模式匹配
    preg:指向编译后的正则表达式的regex_t结构体指针。
	string:要匹配的字符串。
	nmatch:regmatch_t结构体数组的大小,表示最多可以存储的匹配结果数量。
	pmatch:存储匹配结果的regmatch_t结构体数组。
	eflags:匹配选项,可以使用以下常量按位或操作组合:
        REG_NOTBOL:不将字符串的开头视为行的开头。
        REG_NOTEOL:不将字符串的结尾视为行的结尾。
        REG_EXTENDED:使用扩展正则表达式语法。
void regfree(regex_t *preg)
    当不再需要编译的正则表达式时要将其释放
    preg:指向编译后的正则表达式的regex_t结构体指针。
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
    errcode:错误码,通常是regcomp()regexec()的返回值。
	preg:指向编译后的正则表达式的regex_t结构体指针。
	errbuf:存储错误信息的缓冲区。
	errbuf_size:缓冲区的大小。
    
#include 
#include 
#include 

int main(int argc, char** argv)
{
    if(argc != 3)
    {
        printf("Uasge: %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 0;
        }
    }
	// 匹配失败
    unerrmsglen = regerror(nerrcode, &oregex, szerrmsg, sizeof(szerrmsg));
    unerrmsglen = unerrmsglen < sizeof(szerrmsg) ? unerrmsglen : sizeof(szerrmsg) - 1;
    szerrmsg[unerrmsglen] = '\0';
    printf("ErrMag: %s\n", szerrmsg);
    regfree(&oregex);

    return 0;
}

./a.out "^[0-9]{3}$" 123  // 输出 123 matches ^[0-9]{3}$

常用命令

sort命令

命令从标准输入中读取数据然后按照字符串内容进行排序

-f 忽略字符大小写
-n 比较数值的大小
-t 指定分割符,默认是空格或者tab
-k 指定分割后进行比较字段
-u 重复的行只显示一次
-r 反向排序
-R 打乱排序,同样的行洗不乱

cat test.txt | sort -u -k2 -n # 分割后按照数值比较第二列,不重复显示
cat /etc/passwd | sort -t: -k3 -n -u # 给 /etc/passwd 使用用户id排序

uniq

去除重复的行,前提是重复的行连续

-c 显示每行重复的次数
-d 仅显示重复过的行
-u 仅显示不曾重复的行
sort < test.txt | uniq
cat test.txt | sort | uniq

wc

word count 统计数量

-l 统计行数
-c 统计字节数
-w 统计单词数
不加选项,那么就会依次输出行数、单词数、字节数
cat /etc/passwd | wc -l

grep

是一种文本搜索工具,可以使用正则表达式搜索文本,并把匹配的行打印出来。grep家族包含grepegrepfgrepegrepgrep的扩展,支持更多的re元字符,fgrepfixed grep,把所有的字母看作是单词,正则表达式中的元字符表示其自身的字面意义,不在特殊。LinuxGNU版本的grep,功能更加的强大,通过选项 -G-E-F来使用egrepfgrepgrep默认使用的基本的正则。

egrep = grep -E # 扩展正则
fgrep = grep -F
rgrep = grep -r
grep [options]
-c 只输出匹配行的计数
-i 不区分大小写
-h 查询多文件时,不显示文件名
-H 查询多文件时,显示文件名
-l 查询多文件时,只输出包含匹配字符的文件名
-L 列出不匹配的文件名
-n 显示匹配行以及行号
-w 只匹配整个单词,而不是字符串的一部分,例如匹配magic 那么magical就不行
-s 不显示不存在或无匹配文本的错误信息
-v 显示不包含匹配文本的所有行
-r 递归搜索某个目录下的所有文件
-F 不使用正则,使用固定字符串作为匹配
-P 使用perl正则
-E 使用扩展正则
--color=auto 可以将找到的关键字部分加上颜色显示
grep -P "\d\d" test.txt
grep -F "#include" *.c -n -H -w

find

find pathname -options [-print -exec -ok ...]
# 命令参数
-print find命令所查找的目录路径。例如用.表示当前目录,用/表示系统根目录,递归查找
-exec 对匹配的文件执行该参数所给出的shell命令,相应的命令的形式为'command' {} \; ,
	注意{}内部无空格,和\;之间有一个空格。
	find . -type f -exec ls -l {} \; # 每找到一个文件,就会执行exec后面的命令
	find . -name "*.txt" -exec cp {} ./gz/ \;
-ok 和-exec的作用相同,更加安全,在执行每一个命令之前,给出提示,输入y才会执行后面的语句。
	find . -name "*.txt" -ok ls -l {} \;
# options 命令选项
-name 按照文件名查找文件 find .  -name "06*"
-perm 按照文件权限来查找文件 find . -perm 0064
-prune 可以使得find命令不在当前指定目录中查找,同时使用-depth,那么将被忽略
-user 按照文件属主来查找文件 find . -user root
-group 按照文件所属组来查找文件
-mtime -n/+n 按照文件的更改时间来查找文件,
		-n 表示文件更改时间距现在n天以内,+n 表示文件更改时间距现在n天以前
		find . -mtime -2
-atime 和mtine类似,访问时间
-ctime 和mtime类似,创建时间
-nogroup 查找无有效所属组的文件,该文件所属组在/etc/groups中不存在 find ./ -nogroup
-nouser 查找无有效属主的文件,该文件属主在/etc/passwd中不存在 find ./ -nouser
-newer file1 ! file2 查找更改时间比文件file1新但比文件file2旧的文件
-type 查找某一类型的文件
	b 块设备 d 目录 c 字符设备 p 管道文件 l 符号链接文件 f 普通文件
-maxdepth find递归的最大深度
-mindepth find递归的最小深度
-depth find递归的深度
-szie n 查找文件长度为n块的文件 find ./ -size 7c
-fstype 查找某一类型文件系统中的文件
-mount 在查找文件时不跨越文件系统mount点
-follow 遇到符号链接文件跟踪至链接所指向的文件

xargs

将标准输入的参数整齐的拼凑在一行里边。一般需要配合其他命令使用

cat test.txt | xargs touch
# 加入test.txt内容
a
b
c
# 上面的命令解释为 touch a b c, 即将cat test.txt | xargs产生的全部内容放到最后面
find . -name "*.sh" | xargs mv -I{} mv {} ./test
# 其中的-I{} 表示使用{}来表示find . -name "*.sh" | xargs产生的全部内容,假设为a
# 那么就相当于 mv a ./test

sed

sed意为流处理器,将前一个程序的输出引入sed的输入,经过一系列的编辑命令转换为另一种格式输出。文本1 -> sed + 脚本 -> 文本2。sed以行为单位处理文件。

sed option 'script' file1 file2 .... 
sed option -f scriptfile file1 file2 ....
sed '/pattern/actions' file1 file2 ... # pattern正则表达式 actions匹配成功后的动作
sed -n '2p' test.txt # 输出 test.txt 的第二行
sed '2d' test.txt # 删除第二行 
sed '$d' test.txt # 删除最后一行 
sed '2,5d' test.txt # 显示删除2到5行的结果
sed '/12/d' test.txt # 匹配到12就删除该行,显示其他结果
# p(print)打印 i(inserd)插入 a(append)追加 d(delete)删除 s(substition)替换
sed '2i aasscc' test.txt # 在第二行插入 aasscc
sed 's/123/567/' test.txt # 将文本中的123替换为567
sed -n test.txt # 静默输出
-e 允许多个脚本指令被执行
-i 直接修改源文件
-r 在脚本指令中使用扩展正则表达式
-s 将指定的多个文件允许把他们当作单独的文件,正则表达式则不进行跨文件匹配
-u 最低限度的缓存输入输出

awk

不仅能以行为单位还能以列为单位处理文件。awk缺省的行分隔符是换行,缺省的列分隔符是连续空格或者tab。但是行分隔符和列分隔符都可以自己定义。

awk option 'script' file1 file2 ....
awk option -f scriptfile file1 file2 ....
/pattern/{actions} # 满足pattern正则执行actions
condition/{actions} # 满足condition条件执行actions
{actions} # 对每一行都进行处理执行actions
BEGIN {action} # 在遍历文本第一行之前执行某个动作
END {action} # 在遍历完文本执行某个动作

cat /etc/passwd | awk -F: '{print $3}' # -F:分隔符为: 打印第三列 $0表示当前行
awk '{print $1}' test.txt # 打印第一列
awk '/^Pro/{print $2}' test.txt # 开头匹配到Pro的行打印第二列
# 第二列小于75打印当前行和REORDER,大于等于的打印当前行
awk '$2<75 {printf "%s\t%s\n", $0, "REORDER";} $2>=75 {printf "%s\n", $0;}' test.txt
BEGIN{
	printf "%s\t%s\n", "产品", "库存";
	sum=0;
}
{
	printf "%s\t%s" ,$1 ,$2;
	sum+=$2;
}
END{
	printf "库存总量:%d\n", sum;
}

crontab

linux 系统定时器

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
	每小时的第17分钟,root去执行后面的指令
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 
	每天的凌晨6点25分的时候,root去执行后面的指令
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
	每周的星期天的6点47,root去执行后面的指令
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
	每月中的第一天的6点52,root去执行后面的指令

# 先执行crontab –e在添加,或者直接vim /etc/crontab
crontab –e : 修改 crontab 文件. 如果文件不存在会自动创建。
crontab –l : 显示 crontab 文件。
crontab -r : 删除 crontab 文件。
crontab -ir : 删除 crontab 文件前提醒用户。

你可能感兴趣的:(linux,Linux网络编程,linux,运维,服务器)