Vim
Vim模式
一般命令模式:输入"i,o,a"进入编辑模式;输入": / ?"进入命令行模式
编辑模式:ESC进入一般命令模式
命令行模式:ESC进入一般命令模式
Vim操作
补齐
# 文件内容文字
Ctrl + N
# 文件名
Ctrl + F
# 拓展名
Ctrl + O
移动
文首:gg 文尾:G、Shift+g
行首:0 行尾:$、Shift+4
下一页:Ctrl + F
上一页:Ctrl + B
指定行:行号+Shift+g
下移行:行号+Enter
删除
光标位置:行号+x
光标前:d+0
光标后:D
光标所在行:dd 删光标以下5行:5+dd
单词:单词首字符:d+w 删除整个单词
单词中字符:d+w 删除单词后面
复制/粘贴/撤销
#复制:n为行号
nyy
yw # 单词
#剪切
dd
#粘贴
p/P
#撤回
u
可视
# 切换可视模式
v
# 复制y 删除d
查找
/查找内容 #光标向下查找,到最下返头
?查找内容 #光标向上查找,到最上返尾
替换
# ESC切换末行模式
# 光标移动至要修改行
:s/要替换的字符串/填充字符 #第一个替换
:s/要替换的字符串/填充字符/g #整行替换
:@s/要替换的字符串/填充字符/g #全文替换
:27,30s/要替换的字符串/填充字符/g #指定行替换
保存
#ESC切换末行模式
!q #退出
!w #保存
!wq #保存退出
分屏
#ESC切换末行模式
#水平
进入 :sp
切换 Ctrl+ww
保存 :wqall :q :wall :qall
#垂直
进入 :vsp hello.c
切换 Ctrl+ww
保存 :wqall
Shell
Shell简介
# 查看当前环境使用的默认解析器
echo $SHELL # /bin/bash
# 命令查找顺序
1.以相对/绝对路径执行
2.以alias查找执行
3.以bash内置命令执行
4.以$PATH变量顺序查找执行
# 查询命令是否为Shell内置命令
type [-tpa] name
编写习惯
脚本头部信息应当包含:
- 脚本功能
- 版本信息
- 作者及联络
- 脚本历史
- 声明环境变量
#!/bin/bash
#-----------------------------------------------------#
# Program:
# User input his/her first name and last name
# Program shows his/her full name
# OS Version: CentOS 7.x
# Author: Hefery Email: hefery@126.com
# History:
# 2020/08/06 21:37 -- Hefery -- First release
#-----------------------------------------------------#
PATH=/bin:/sbin:/user/bin:/user/sbin:/user/local/bin:/user/local/sbin:~/bin
第一行 #!/bin/bash 声明脚本使用的shell名称 #!加载bash相关配置文件
脚本变量
变量的使用
# 查看系统环境变量
env
HOSTNAME # 主机名
SHELL # 使用的Shell
USER # 使用者
PATH # 环境变量
LANG # 语系
HOME # 根目录
# 定义并设置
val_name=Hefery
# 删除变量
unset mval_nameyname
# 定义环境变量
echo ${val_name}
# 设置常量
readonly val_name
变量设置规则:
- “=” 两边不能直接有 space
- 变量名只能以字母开头,可使用数字
- 变量内有 space 可以用双引号[""]、单引号['']
- 可用转义符[/]表示特殊字符
环境变量
自定义系统环境变量:
- 初始化Shell环境时会加载全局配置文件/etc/profile
- 编辑/etc/profile文件 -> source /etc/profile
Shell登录环境
- 判断:echo $0
-bash:Shell登录环境
bash:Shell非登录环境
- 切换:
su 用户名 -l # 切换到指定用户,加载Shell登录环境
su 用户名 # 切换到指定用户,加载非Shell登录环境
环境变量加载流程:
- Shell登录环境 -> 执行/etc/profile加载系统环境变量 -> 执行/etc/profile.d/*.sh加载系统环境变量 ->
执行用户/.bash_profile加载用户环境变量 -> 加载用户/.bashrc加载用户环境变量 -> 执行/etc/basgrc加载系统环境变量
- 非Shell登录环境 -> 加载用户/.bashrc加载用户环境变量 -> 执行/etc/basgrc加载系统环境变量 ->
执行/etc/profile.d/*.sh加载系统环境变量
特殊变量
$n:执行脚本传入参数
script.sh opt1 opt2 opt3 opt4 ...
脚本文件名$0 $1 $2 $3 $4
$#:获取输入参数个数 4
$@:获取以后所有输入参数 "$1" "$2" "$3" "$4"
$*:获取当前所有输入参数 "$1c$2c$3c$4", c为分隔符,默认为空格
$?:获取上一个Shell命令的退出状态码或函数返回值
$$:获取当前Shell环境的进程ID
字符串
截取:
${val_name:start:length}
${val_name:start}
${val_name:0-start:length}
${val_name:0-start}
${val_name#*chars}
${val_name##*chars}
${val_name%chars*}
${val_name%%chars*}
数组Array
Bash Shell只支持一维数组
数组定义:
array_name=(item1 item2 ...)
array_name([index1]=item1 [index2]=item2 ...)
数组属性:
单个数组元素:${array_name[index]]
所有数组元素:${array_name[@ | *]}
获取数组长度:${#array_name[@ | *]}
数组拼接:
array_name=(${array1[@]} ${array1[#]})
数组删除:
删除单个元素:unset array_name[index]
删除整个数组:unset array_name
特殊符号
符号 | 意义 |
* | 任意多个字符 |
? | 任意一个字符 |
[ - ] | 有减号:编码顺序内的所有字符 [0-9] |
[^ ] | 中括号第一个字符为“^”,反向选择 [^abc]:一定有一个字符是非a、b、c的其它字符 |
符号 | 意义 |
# | 注释符 |
\ | *转义符:将特殊字符或通配符转换为一般字符 |
| |
; | 连续命令执行分隔符 |
~ | 用户家目录 |
$ | 使用变量前导符 |
& | 任务管理:将命令变成后台命令 |
! | 逻辑运算:非 |
/ | 路径分隔符 |
>,>> | 数据重定向:输出定向 分别是替换、累加 |
<,<< | 数据重定向:输入定向 |
‘ ’ | 单引号,不具有变量替换功能 |
“ ” | 双引号,具有变量替换功能 |
| 中间是可先执行的命令,相当于 $( ) |
( ) | 中间为子shell的起始和结束 |
{ } | 中间为命令快 |
RE字符 | 意义 |
^word | 待查找的word在行首 |
word$ | 待查找的word在行尾 |
. | 一定存在的一个任意字符 |
\ | 转义 |
* | 任意多个字符 |
[list] | *list:想要取的字符 [aliy] |
[n1-n2] | 想要选取的字符范围 [0-9]、[a-z]、[A-Z] |
[^list] | 不要出现的字符/范围 |
{n,m} | 连续n-m个字符 |
+ | *重复前RE字符/字符串 ‘go+d’:god、good、goood |
? | 前一个RE字符 |
| | 逻辑或 ‘gd| good | dog ’ |
( ) | 群组字符串 ‘g(la |
( ) + | *多个重复群组 ‘A(xyz)+C’:A开头,C结尾,中间有一个以上xyz字符串 |
运算符
算数运算符
expr:求值表达式 result=`expr 算数表达式`
运算符:+ - * / % =(赋值)
比较运算符
整数比较
数学计算:((表达式))
整数运算:$[表达式]
运算符 | 说明 | 样例 |
-eq、== | 等于 | [$a -eq $b] |
-ne、!= | 不等于 | [$a -ne $b] |
-gt、> | 大于 | [$a -gt $b] |
-lt、< | 小于 | [$a -lt $b] |
-ge、>= | 大于等于 | [$a -ge $b] |
-le、<= | 小于等于 | [$a -le $b] |
字符串比较
字符串比较:[[]]、[]
1.字符分隔比较:[[]]不会
2.是否支持转义:[[]]不用转义,[]手动转义
运算符 | 说明 | 样例 |
==、= | 等于 | |
!= | 不等于 | |
< | 小于 | [$a<$b ] |
> | 大于 | |
-z | 检查字符串长度是否为0,为0返回true | [ -z $a ] |
-n | 检查字符串长度是否不为0,不为0返回true | [ -n $a ] |
逻辑运算符
布尔
布尔运算符 | 说明 | 样例 |
! | 取反 | |
-o | or或 | |
-a | and与 | |
逻辑
逻辑运算符 | 说明 | 样例 |
&& | 逻辑AND | [[ 表达式1 && 表达式2 ]] |
|| | 逻辑OR | [[ 表达式1|| 表达式2 ]] |
! | 非 | [[ !表达式 ]] |
文件运算符
运算符 | 说明 | 样例 |
-b file | 检测文件是否为块设备文件,是返回true | [[ -b &file ]] |
-c file | 检测文件是否为字符设备文件,是返回true | [[ -c &file ]] |
-d file | 检测文件是否为目录,是返回true | [[ -d &file ]] |
-f file | 检测文件是否为普通文件(非设备文件和目录),是返回true | [[ -f &file ]] |
-g file | 检测文件是否设置了SGID位,是返回true | [[ -g &file ]] |
-k file | 检测文件是否设置了粘着位(Stick Bit),是返回true | [[ -k &file ]] |
-p file | 检测文件是否为管道文件,是返回true | [[ -p &file ]] |
-u file | 检测文件是否设置了SUID位,是返回true | [[ -u &file ]] |
-r file | 检测文件是否可读,是返回true | [[ -r &file ]] |
-w file | 检测文件是否可写,是返回true | [[ -w &file ]] |
-x file | 检测文件是否可执行,是返回true | [[ -x &file ]] |
-s file | 检测文件是否为空(文件大小为0),不为空返回true | [[ -s &file ]] |
-e file | 检测文件或目录是否存在,存在返回true | [[ -e &file ]] |
file1 -nt file2 | new than,file1是否比file2新 | [[ file1 & file2 ]] |
file1 -ot file2 | old than,file1是否比file2旧 | [[ file1 & file2 ]] |
逻辑语句
命令替换:赋值等号和替换字符间没有空格
- 反引号(``):test=`date`
- $():test=$(date)
条件
判断:[ "${变量}" == "XXX" ] # 注意空格
read -p "Please input (Y/N):" yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "OK" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "OH" && exit 0
条件:
# 1.if...then...fi
# 简单判断:if [ 条件判断式 ]; then ... fi
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]
then
echo "OK, continue"
exit 0
fi
# 复杂判断:if [ 条件判断式 ]; then ... elif ... else ... fi
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]
then
echo "OK, continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]
then
echo "OH, interrupt"
else
echo "I don't know"
fi
# 2.case...esac
case ${1} in
"one")
echo "The one"
;;
"two")
echo "The two"
;;
*)
echo "Others"
;;
esac
循环
# 1.while/until...do...done
# 满足 condition 进入循环,否则跳过循环
while [ condition ]
do
...
continue # 结束当前循环,接入下一次循环
break # 结束当前循环
done
s=0
i=0
while [ "${i}" != "100" ]
do
i=$(($i+1))
s=$(($s+$i))
done
# 满足 condition 终止循环,否则继续循环
until [ condition ]
do
...
done
# 2.for...do...done
# 字符处理
for var in [col1 col2 col3... | {start..end}]
do
...
done
for (( i=start; i<=end; i=i++ ))
do
...
done
users=$(cut -d ":" -f1 /etc/passwd)
for username in ${users}
do
id ${username}
done
# 数值处理
for (( 初始值; 限制值; 赋值运算 ))
do
...
done
s=0
for (( i=1; i<=${nu}; i=i+1 ))
do
s=$((${s}+${i}))
done
for i in {1..5}
do
echo $i
done
函数
Shell脚本与函数的区别:
Shell程序/命令会在子Shell中运行,会开启独立的进程运行;Shell函数在当前的Shell中运行
通过自定义函数实现代码复用
# shell脚本由上而下、由左而右执行,函数一定要设置在程序前面
function funname() {
...
[return]
}
# 调用函数
funname 参数1 参数2
函数参数 | 说明 |
$# | 传递到脚本或函数的参数个数 |
$*、$@ | 以一个单字符串显示所有传入参数 |
$$ | 脚本运行的当前进程ID |
$! | 后台运行的最后一个进程ID |
$? | 显示最后命令的退出状态 |
数据重定向
将某个命令执行后应该出现在屏幕上的数据传输到其它地方
1.标准输入 stdin:代码为0 使用 < 或 << Shell程序默认会从stdin文件最后读取输入数据
2.标准输出 stdout:代码为1 使用 > 或 >> Shell程序默认会向stdout文件输出正确数据
3.标准错误输出stderr:代码为2 使用 2> 或 2>> Shell程序默认会向stderr文件输出错误信息
< :覆盖,若文件不存在则自动创建
<< :追加,若文件不存在则自动创建
# 将 ll 命令的输出覆盖掉根目录下的 rootfile 文件中
ll / > ~/rootfile
# 将 ll 命令的输出追加到根目录下的 rootfile 文件中
ll / > ~/rootfile
# 将正确信息和错误信息分开定向
ll / > ~/log_right 2> log_error
# 丢弃错误信息,正确信息输出到屏幕
ll / > ~/rootfile 2> /dev/null
# 正确、错误信息全部存到一个 list 文件
ll / > ~/rootfile > list 2> list # 错:两股数据可能会交叉写入,造成次数错乱
ll / > ~/rootfile > list 2>&1 # 对
ll / > ~/rootfile &> list # 对
脚本执行
脚本调试方式
sh [-nvx] scripts.sh
-n:不执行,仅查询语法问题
-v:执行脚本前,先将脚本文件内容输出屏幕
-x:将脚本文件内容输出屏幕
# sh -x scripts.sh
脚本执行方式
1. source script:在父进程中执行
2. sh script:
3. ./script:先赋权限
内置命令
内置命令 | | |
alias | 起别名 | alias 别名=“命令” |
unalias | 删除别名 | unalias 别名;unalias -a |
read | 读取控制台输入 | |
bind | 显示当前关键字与函数的绑定情况,或将关键字与readline函数或宏进行绑定 | |
break | 从最内层循环跳出 | |
cd | 改变目录 | |
declare | 显示所有变量,或用可选属性声明变量 | |
echo | 输出显示 | |
exec | 运行命令 | |
exit | 退出Shell | |
export | 声明变量 | |
help | 显示关于内置命令的有用信息 | |
history | 显示带行号的命令历史列表 | |
kill | 向由PID号或作业号指定的进程发送信号 | |
let | 用来计算算术表达式的值,并把算术运算的结果赋给变量 | |
local | 用在函数中,把变量的作用域限制在函数内部 | |
logout | 退出登录Shell | |
pwd | 当前的工作目录 | |
readonly | 将变量var设为只读,不允许重置该变量 | |
return | 从函数中退出 | |
set | 设置选项和位置参量 | |
shift | 将位置参量左移n次 | |
stop pid | 暂停第pid号进程的运行 | |
suspend | 终止当前Shell的运行 | |
test | 检查文件类型,并计算条件表达式 | |
times | 显示由当前Shell启动的进程运行所累计用户时间和系统时间 | |
type | 显示命令的类型 | |
typeset | 同declare。设置变量并赋予其属性 | |
ulimit | 显示或设置进程可用资源的最大限额 | |
umask | 用户文件关于属主、属组和其他用户的创建模式掩码 | |
unset | 取消指定变量的值或函数的定义 | |
wait | 等待pid号为n的后台进程结束,并报告它的结束状态 | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
exit:退出,并返回一个状态码
应用:
结束当前Shell进程
状态码:
0:命令执行成功
非0:命令执行失败
expr:
计算字符串长度:expr length 字符串
截取字符串:expr substr 字符串 start end
获取在字符串出现位置:expr index 字符串 substr
select:
菜单选择输入,无限循环,Ctrl + D结束循环
select val in menu1 menu2...
do
...
done
test:整数、字符串、文件对比
整数:
if test num1 options num2 # options:-eq -ne -gt -ge -lt -le
then
...
fi
字符串:
=、==:对于返回0表示成功
!=:不等于
<:小于
>:大于
-z 字符串:字符串长度为0为true
-n 字符串:字符串长度不为0为true
文件:
-e 文件名:exist,文件存在为true
-r 文件名:read,文件存在且可读为true
-w 文件名:write,文件存在且可写为true
-x 文件名:execute,文件存在且可执行为true
-s 文件名:string,文件存在且至少有一个字符为true
-d 文件名:directory,文件存在且为目录为true
-f 文件名:file,文件存在且为普通文件为true
-d 文件名:文件存在且可块文件为true
declare:设置变量属性 declare [+/-][aArif][val_name=val_value]
+:设置属性
-:取消属性
a array:设置为普通索引
A Array:设置为key-value关联数组
r readonly:设置变量为只读
x export:设置变量为环境变量
i int:设置变量为整型
f function:设置为函数变量
read:读取控制台输入
参数:
-a array:读取的数据赋值给数组,index从0开始
-d delimiter:用字符串delimiter读取指定结束的位置
-n num:读取num个字符
-p prompt:显示信息,提示内容为prompt
-r:原样读取,不会转义
-s:静默模式,不会在控制台输出输入内容
-t seconds:设置超时时间
-u fd:使用文件描述符fd作为输入源
提示信息:read -p "请输入姓名,年龄,爱好:" name age hobby
读取单个字符:read -n 1 -p "是否要删除数据(y/n)" char
核对密码:
read -t 20 -sp "请输入密码(20S)" pwd1
read -t 20 -sp "请再次输入密码(20S)" pwd2
if [ ${pwd1} == ${pwd2} ]
then
echo "密码正确"
else
echo "密码错误"
fi
文本处理
grep查找
属性选项:
-r :递归搜索
-i :忽略大小写(ignore case)
-v :反过来(invert),只打印没有匹配的,而匹配的反而不打印
-n :显示行号
-w :被匹配的文本只能是单词,而不能是单词某一部分,如文本中有liker,而我搜寻的只是like,就可以使用-w选项来避免匹配liker
-c :显示总共有多少行被匹配到了,而不是显示被匹配到的内容,注意如果同时使用-cv选项是显示有多少行没有被匹配到
-o :只显示被模式匹配到的字符串
-color :将匹配到的内容以颜色高亮显示
-E :开启扩展(Extend)的正则表达式
-A n:显示匹配到的字符串所在的行及其后n行,after
-B n:显示匹配到的字符串所在的行及其前n行,before
-C n:显示匹配到的字符串所在的行及其前后各n行,context
find查找
查找文件:
当前工作目录:find . -name file_name
指定路径查找:
find /hefery/devhb -name file_name
find /hefery/devhb -type f -name file_name
忽略大小写查:find /hefery/devhb -iname file_name
根据权限查找:
find /hefery/devhb -type f -perm 0777 -print # 权限为777
find /hefery/devhb -type f ! -perm 777 # 没有 777 权限
find / -type f -perm 0777 -print -exec chmod 644 {} \ # 权限为 777 且 chmod 为 644 的文件
查找只读文件:find /hefery/devhb -perm /u=r
查找可执行文件:find /hefery/devhb -perm /a=x
查找和删除多个文件:find . -type f -name "*.txt" -exec rm -f {} \
查找所有空文件:find /tmp -type f -empty
查找最近 50 天修改过的文件:find / -mtime 50
查找最近 50 天访问过的文件:find / -atime 50
查找最近 50-100 天修改过的文件:find / -mtime +50 –mtime -100
查找过去 1 小时内更改过的文件:find / -cmin -60
查找过去 1 小时内访问过的文件:find / -amin -60
找到 50MB 的文件:find / -size 50M
查找 50MB – 100MB 之间的大小:find / -size +50M -size -100M
查找和删除 100MB 文件:find / -type f -size +100M -exec rm -f {} \
查找特定文件并删除:find / -type f -name *.txt -size +10M -exec rm {} \
查找目录:
find /hefery/devhb -type d -name dir_name
find / -type d -perm 777 -print -exec chmod 755 {} \ # 查找权限为 777 且 chmod 为 755 的目录
find /tmp -type d -empty# 查找所有空目录
wc统计
文本统计:
单词个数:wc -w < hello.txt
单词行数:wc -l < hello.txt
单词字节数:wc -c < hello.txt
cut列处理
提取Bash进程的PID:
ps -aux | grep bash
提取IP地址:
ifconfig | grep broadcast | cut -d " " -f 10
参数选项 | 说明 | |
-f 提取范围 | 以列号为单位进行切割 | |
-c 提取范围 | 以字符为单位进行切割 | |
-b 提取范围 | 以字节为单位进行切割 | |
-d 自定义分隔符 | 默认为制表符 | |
提取范围 | 说明 |
n- | 第n[列] |
n-m | 第n到第m[列 字符 字节]中间的数据 |
-m | 第m[列] |
n1,n2… | 指定枚举值获取列 |
sed编辑
文本流处理器:对文本文件的每一行数据匹配后进行CRUD
sed 参数选项 [模式匹配 | sed程序命令] 文件名
添加数据:
sed "3ahefery" sed.txt # 在第三行后添加hefery,没有修改源文件
sed "/devhb/ahefery" sed.txt # 在内容devhb后添加hefery
sed "/devhb/ihefery" sed.txt # 在内容devhb前添加hefery
删除数据:
sed "2d" sed.txt # 删除第二行数据
sed "1~2d" sed.txt # 删除奇数行数据:第一行开始,每隔2行删除一行
sed "1,3d" sed.txt # 删除第一行到第三行的数据
sed "$d" sed.txt # 删除最后一行数据
sed "/hefery/d" sed.txt # 删除匹配hefery的行数据
sed "/hefery/,d" sed.txt # 删除匹配hefery的行到最后一行的数据
sed "/hefery/,+1d" sed.txt # 删除匹配hefery的行到下一行的数据
sed "/hefery\|hefery/!d" sed.txt # 删除匹配hefery的行到最后一行的数据
修改数据:
sed "1chefery" sed.txt # 将文件的第一行修改为hefery
sed "$chefery" sed.txt # 将文件中的最后一行修改为hefery
sed "/hefery/chello" sed.txt # 将文件中的hefery修改为hello
sed -n "s/hefery/hello/2pw sed2.txt" sed.txt # 将第2个匹配的hefery替换为hello,后的内容写入文件
sed -n "s/hefery/hello/2p" sed.txt > sed2.txt # 将第2个匹配的hefery替换为hello,后的内容写入文件
sed "s/$/& test" sed.txt # 将每行的末尾拼接hefery
sed "s/^/#/' test" sed.txt # 将每行的行首添加注释#
查询数据:
sed -n '/hefery/p' sed.txt # 查询含有hefery的行
第一行删除,并将hefery替换为hello:
sed -e '1d' -e 's/hefery/hello/g' sed.txt
sed '1d;s/hefery/hello/g' sed.txt
参数选项 | 说明 |
-e | 直接在指定列模式上进行sed动作编辑 |
-i | 对内容进行修改 |
-f | 后跟保存了sed指令的文件 |
-n | 取消默认输出,只显示处理过的行 |
-r | 使用拓展正则表达式 |
sed程序命令 | 说明 |
a | 新增add |
c | 修改change |
d | 删除delete |
i | 插入insert |
p | 打印print |
s | 替换substitute |
= | 打印被匹配行的行号 |
n | 读取下一行. |
awk分析
文本分析工具:
awk 'pattern{action}' {filename}
参数选项 | 说明 |
-F | 指定输入文件拆分分隔符 |
-V | 赋值一个用户定义变量 |
内置变量 | 说明 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数排列 |
ENVIRON | 支持队列中系统环境变量的使用 |
FILENMAE | awk浏览的文件名 |
FNR | 浏览文件的记录数 |
FS | 设置输入域分隔符 |
NF | 浏览记录的域的个数,根据分隔符分隔后的列数 |
NR | 已读的记录数 |
OFS | 输出域分隔符 |
ORS | 输出记录分隔符 |
RS | 控制记录分隔符 |
$n | **$0:整条记录 **$**1:当前行的第一个域 **$2:当前行的第二个域 |
$NF | 最后一列的信息 |
sort排序
参数选项 | 说明 | 样例 |
-n | 依照数值大小排序 | |
-r | 以相反的顺序排序 | |
-t | 设置排序时设置的分隔符 | |
-k | 指定需要排序的列 | |
-d | 排序时,除英文、数字和空格字符外概不处理 | |
-f | 排序时,将小写视为大写 | |
-b | 忽略每行前面开始的空格 | |
-o | 排序的结果输出到文件 | |
-u | 输出结果去重处理 | |
-m | 合并排序好的文件 | |