0033. shell命令--sort

目录

33. shell命令--sort

功能说明

语法格式

选项说明

实践操作

注意事项


33. shell命令--sort

功能说明

        sort 是 Linux 和 Unix 系统中的一个常用命令,用于对文本文件的行进行排序。

        sort 命令的功能是对文件内容进行排序。有时文本中的内容顺序不正确,一行行地手动修改实在太麻烦了。此时使用 sort 命令就再合适不过了,它能够对文本内容进行再次排序。

语法格式

SYNOPSIS
       sort [OPTION]... [FILE]...
       sort [OPTION]... --files0-from=F

选项说明

-b, --ignore-leading-blanks:忽略每行前的空格字符。
-c, --check, --check=diagnose-first:检查输入是否已排序;如果不排序,则报告第一个失序行。
-C, --check=quiet, --check=silent:似于 -c 选项,但不输出第一个未排序的行。
-d, --dictionary-order:仅考虑空白、字母和数字字符,忽略其他字符。
-f, --ignore-case:忽略大小写。
-g, --general-numeric-sort:按数值比较,忽略空格和逗号等。
-h, --human-numeric-sort:按人类可读的数字(如 2K 1G)排序。
-i, --ignore-nonprinting:考虑不可打印字符。
-k,--key=KEYDEF,POS1[,POS2]:根据指定的字段位置进行排序。例如,-k 2,2n 表示按第二列进行数值排序。
-m, --merge:合并已排序的文件。
-M, --month-sort:按月份名排序(如 JAN, DEC)。
-n, --numeric-sort:按数值进行排序。
-o FILE, --output=FILE:将结果写入 FILE 而不是标准输出。
-r, --reverse:逆序排序。
-R, --random-sort:随机排序(依据随机哈希值),但分组相同的行。
-s, --stable:在排序后保留相同的行的原始顺序(稳定排序)。
-t CHAR, --field-separator=CHAR:使用 CHAR 代替空格或制表符作为字段分隔符。
-T, --temporary-directory=DIR:使用DIR作为临时目录,而不是 $TMPDIR 或 /tmp;多次使用该选项指定多个临时目录。
-u, --unique:仅输出唯一的行(相当于 uniq)。
-v, --version:显示版本信息。
-z, --zero-terminated:以空字符(null)结束行,而不是换行符。
--sort=WORD:根据WORD排序,其中: general-numeric 等价于 -g,human-numeric 等价于 -h,month 等价于 -M,numeric 等价于 -n,random 等价于 -R,version 等价于 -V。
--compress-program=PROG:使用PROG压缩临时文件;使用PROG -d解压缩。

--debug:注释用于排序的行,发送可疑用法的警报到stderr。
 --files0-from=F:从文件F中读取以NUL结尾的所有文件名称;如果F是 - ,那么从标准输入中读取名字
 --parallel=N:将并发运行的排序数更改为N。
 
KEYDEF的格式为:F[.C][OPTS][,F[.C][OPTS]] ,表示开始到结束的位置。
F表示列的编号
C表示OPTS为[bdfgiMhnRrV]中的一到多个字符,用于覆盖当前排序选项。
使用--debug选项可诊断出错误的用法。

SIZE 可以有以下的乘法后缀:
% 内存的1%;
b 1;
K 1024(默认);
剩余的 M, G, T, P, E, Z, Y 可以类推出来。

返回值
返回0表示成功,返回非0值表示失败。

实践操作

0. 环境准备 
mkdir -p /test/sort
cd /test/sort

cat >file1.txt <<'eof'
aaa:10:1.1
ccc:30:3.3
ddd:40:4.4
bbb:20:2.2
eee:50:5.5
eee:50:5.5
eof

cat >file2.txt <<'eof'
No1:No2:No3
aaa:30:1.6
ccc:50:3.3
ddd:20:4.2
bbb:10:2.5
eee:40:5.4
eee:60:5.1
eof

cat >file3.txt <<'eof'
4
 2
1
 3
eof

head -v file*

1. 按默认方式排序
cat file1.txt
sort file1.txt

2. 忽略相同行使用 -u 选项或者 uniq
cat file1.txt
sort file1.txt
sort -u file1.txt
uniq file1.txt

3. sort的-n、-r、-k、-t选项的使用
3.1 将 No2 列按照数字从小到大顺序排列
cat file2.txt
sort file2.txt
sort -nk 2 -t: file2.txt

3.2 将 No3 列数字从大到小顺序排列:
# -n是按照数字大小排序,-r是以相反顺序,
# -k是指定需要排序的栏位,-t指定栏位分隔符为冒号号
cat file2.txt
sort -nrk 3 -t: file2.txt

4. 忽略每行前的空格,按数值排序
cat file3.txt
sort -b file3.txt    #-b 忽略每行前的空格字符
sort -bn file3.txt    #-n 按数值进行排序

5. 忽略每行前的逗号,按数值排序
echo -e ",6\n ,5" >>file3.txt
cat file3.txt 
sort -b file3.txt 
sort -bn file3.txt
sort -bd file3.txt    #-d 仅考虑空白、字母和数字字符,忽略其他字符

6. 再试下逆序排序
cat file3.txt
sort file3.txt
sort -r file3.txt
sort -brd file3.txt

7. 将结果写入另一个文件
sort -bd file3.txt
sort -bd file3.txt -o sorted_file1.txt
cat sorted_file1.txt
sort -brd file3.txt > sorted_file2.txt 
cat sorted_file2.txt

注意事项

关于-k选项的解读和例子:

-k选项深度解读:

FStart.CStart Modifier,FEnd.CEnd Modifier
-------Start--------,-------End--------
 FStart.CStart 选项  ,  FEnd.CEnd 选项

        这个语法格式可以被其中的逗号,分为两大部分,Start 部分和 End 部分。 Start 部分由三部分组成,其中的 Modifier 部分就是我们之前说过的选项部分; 我们重点说说 Start 部分的 FStart 和C.Start;C.Start 是可以省略的,省略的话就表示从本域的开头部分开始。FStart.CStart,其中 FStart 就是表示使用的域,而 CStart 则表示在 FStart 域中从第几个字符开始算排序首字符。 同理,在 End 部分中,你可以设定 FEnd.CEnd,如果你省略 .CEnd 或将它设定为 0,则表示结尾到本域的最后一个字符。

例子:从公司英文名称的第二个字母开始排序:

$ sort -t ' ' -k 1.2 facebook.txt
baidu 100 5000
sohu 100 4500
google 110 5000
guge 50 3000

        解读:使用了-k 1.2,表示对第一个域的第二个字符开始到本域的最后一个字符为止的字符串进行排序。你会发现baidu因为第二个字母是a而名列榜首。sohu 和 google 第二个字符都是 o,但 sohu 的 h在 google 的 o 前面,所以两者分别排在第二和第三。guge 只能屈居第四了。

例子:只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:

$ sort -t ' ' -k 1.2,1.2 -nrk 3,3 facebook.txt
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000

        解读:由于只对第二个字母进行排序,所以我们使用了 -k 1.2,1.2 的表示方式,表示我们只对第二个字母进行排序(如果你问我使用 -k 1.2 怎么不行?当然不行,因为你省略了 End 部分,这就意味着你将对从第二个字母起到本域最后一个字符为止的字符串进行排序)。 对员工工资进行排序,我们也使用了 -k 3,3,这是最准确的表述,表示我们只对本域进行排序,因为如果你省略了后面的 3,就变成了我们对第 3 个域开始到最后一个域位置的内容进行排序了。

注意

  • 关于-g和-n选项的区别:stackoverflow
  • 关于这个复杂命令的学习,建议您阅读info文档及参考博客、问答网站等。
  • 该命令是GNU coreutils包中的命令,相关的帮助信息请查看man -s 1 shuf,info coreutils 'shuf invocation'。
<think>嗯,用户需要处理/log目录下从20170606到20170612的日志文件,按天打包压缩,并删除源文件。首先,我得确定如何提取日期部分。文件名里有类似base.log.2017-06-06.3152这样的结构,日期格式是YYYY-MM-DD。所以需要从文件名中提取出日期部分,比如2017-06-06。 接下来,应该遍历这些文件,按日期分类。可以用awk或者cut来分割文件名,提取日期。例如,用awk分割点号,第三个字段就是日期。或者用cut -d '.' -f3。不过要注意文件名中的其他部分是否有干扰,比如可能有多个点号的情况。 然后,需要为每个唯一的日期创建一个压缩包,把对应日期的文件加进去。这里可以用循环,先收集所有日期,去重,再逐个处理。比如用ls列出所有文件,提取日期,用sort -u去重,然后循环每个日期,打包该日期的所有文件。 打包时使用tar命令,参数czf创建gzip压缩的文件,比如log.2017-06-06.tar.gz。打包完成后删除源文件,用rm -f确保删除。需要注意的是,如果文件名中有空格或其他特殊字符,要处理好引号,避免解析错误。 另外,用户给出的例子中有文件名存在空格,比如“base. log.2017-06-10.747”,中间有个空格,这可能是个打字错误,但在实际处理中可能需要处理这种情况,或者用户提供的文件名是否真的存在这样的问题。这时候可能需要更稳健的文件名处理方式,比如使用find命令加上-print0来处理带空格的文件名,但可能会增加脚本复杂度。 还要考虑日期范围,用户需要的是20170606到20170612,但给出的例子中有2017-06-13的文件,是否需要排除?用户的问题描述里说“现需要将这些文件按天打包压缩”,可能是指所有文件,但例子中可能包含超出范围的日期,需要根据实际情况判断是否需要过滤日期范围。如果确实需要限定日期范围,应该在提取日期后,进行日期比较,只处理在指定范围内的日期。 不过用户的问题描述中并没有明确提到需要过滤日期,只是说目录下有从20170606到20170612的文件,可能意味着只需要处理这些日期。但实际脚本可能需要先提取所有文件的日期,然后处理所有存在的日期,不管是否在范围内,或者需要检查日期是否在指定范围内。需要进一步确认用户的需求,但根据问题描述,可能不需要特别过滤,只是按文件实际存在的日期打包。 另外,处理文件时,是否要包括所有以base.log开头的文件?比如例子中的base.log.2017-06-06.3152和base.log.2017-06-06.5312,这些文件都属于同一天,应该被打包到同一个tar.gz文件中。所以需要正确匹配这些文件。 最后,删除源文件需要在打包成功之后进行,避免打包过程中出错导致文件丢失。所以在tar命令成功执行后,再删除对应的文件。可以使用&&操作符,即tar成功则执行rm。 综上,脚本的大致步骤是: 1. 遍历/log目录下的所有符合条件的日志文件。 2. 提取每个文件的日期部分。 3. 收集所有唯一的日期。 4. 对每个日期,打包该日期对应的所有文件,压缩为log.日期.tar.gz。 5. 删除已打包的文件。 可能的陷阱包括文件名中的空格、日期提取错误、打包时遗漏文件或包含多余文件,以及处理大量文件时的性能问题。需要测试脚本,确保正确处理各种情况。</think>以下是一个实现按天打包压缩日志文件并删除源文件的Shell脚本解决方案,包含详细步骤说明: --- ### **一、需求分析** 目标:将`/log`目录下形如`base.log.2017-06-06.xxxx`的文件按日期打包为`log.2017-06-06.tar.gz`,并删除源文件。 --- ### **二、分步骤实现** #### **1. 提取所有日期并去重** ```bash #!/bin/bash LOG_DIR="/log" # 获取所有唯一日期(格式:YYYY-MM-DD) dates=$(ls "$LOG_DIR" | grep -oE '20[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort -u) ``` #### **2. 遍历日期进行打包** ```bash for date in $dates; do # 构造目标压缩文件名 archive="log.${date}.tar.gz" # 查找该日期所有文件(精确匹配日期段) files=$(find "$LOG_DIR" -name "base.log.${date}.*") # 打包并压缩 if [ -n "$files" ]; then echo "正在打包 $date 的日志..." tar czf "${LOG_DIR}/${archive}" $files # 删除源文件(仅在打包成功后执行) if [ $? -eq 0 ]; then rm -f $files echo "$date 文件已压缩并删除源文件" else echo "$date 打包失败,保留源文件" fi fi done ``` --- ### **三、完整脚本** ```bash #!/bin/bash LOG_DIR="/log" # 获取所有唯一日期(格式:YYYY-MM-DD) dates=$(ls "$LOG_DIR" | grep -oE '20[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort -u) for date in $dates; do archive="${LOG_DIR}/log.${date}.tar.gz" files=$(find "$LOG_DIR" -name "base.log.${date}.*") if [ -n "$files" ]; then echo "打包: $date" tar czf "$archive" $files if [ $? -eq 0 ]; then rm -f $files echo "成功: ${archive} 已创建,源文件已删除" else echo "错误: ${date} 打包失败" fi else echo "警告: 未找到 $date 的日志文件" fi done ``` --- ### **四、关键点解析** 1. **日期提取** - 使用正则表达式`grep -oE '20[0-9]{2}-[0-9]{2}-[0-9]{2}'`精确匹配日期格式 - `sort -u`去重避免重复处理 2. **文件匹配** - `find "$LOG_DIR" -name "base.log.${date}.*"`确保精确匹配目标日期的所有文件 3. **错误处理** - `$? -eq 0`检查`tar`命令是否执行成功 - 仅在打包成功后删除源文件(`rm -f $files`) --- ### **五、注意事项** 1. **权限问题** 确保脚本有权限操作`/log`目录,建议提前测试: ```bash chmod +x pack_logs.sh # 添加执行权限 ./pack_logs.sh # 测试运行 ``` 2. **文件名规范** 如果日志文件名包含空格,需要修改`find`命令为: ```bash find "$LOG_DIR" -name "base.log.${date}.*" -print0 | xargs -0 tar czf "$archive" ``` 3. **日期范围过滤(可选)** 如果需要限制为2017-06-06到2017-06-12: ```bash start_date="2017-06-06" end_date="2017-06-12" dates=$(ls "$LOG_DIR" | grep -oE '20[0-9]{2}-[0-9]{2}-[0-9]{2}' | sort -u | awk -v s="$start_date" -v e="$end_date" '$0 >= s && $0 <= e') ``` --- ### **六、扩展改进建议** 1. **日志记录** 添加日志记录功能: ```bash exec >> "${LOG_DIR}/pack.log" 2>&1 # 重定向输出到日志文件 ``` 2. **定时任务** 通过`crontab`每日自动执行: ```bash 0 2 * * * /path/to/pack_logs.sh # 每天凌晨2点执行 ``` --- 通过这个脚本,可以实现对日志文件的自动化按天归档管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MineGi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值