我想根据列表名称的日期时间部分对其进行排序。
使用排序可以吗?我无法指定排序列,因为该列可能会有所不同,如下面的示例输入所示。
swid_ds_install_user_20171227172654_20425.log
package_user_20171227172949_5627.log
swid_state_definition_user_20171227162839_6515.log
swid_ds_install_user_20171227172732_23839.log
swid_appsrv_stop_user_20171227172258_27116.log
package_user_20171227172610_16198.log
swid_state_definition_user_20171227172344_322.log
package_user_20171227233634_23845.log
package_user_20171227162858_7082.log
我可以通过例如反转字段的顺序
awk -F_ '{for (i=NF;i>0;i--){printf $i"_"};printf "\n"}'
然后用 -d_ -k2,2 排序,然后反转字段顺序以保留原始文件名 - 使用例如 sed 清除残留分隔符 - 但这会变得很尴尬。
awk -F_ '{for (i=NF;i>0;i--){printf $i"_"};printf "\n"}' | sort -t'_' -k2,2 \
| awk -F_ '{for (i=NF;i>0;i--){printf $i"_"};printf "\n"}' | sed 's/^_//' \
| sed 's/_$//'
你会如何处理这个问题?
我正在考虑使用 sed 通过正则表达式来分解日期时间部分,并将其进行排序,然后在打印输出时使用一些内置函数来恢复完整的文件名,而不仅仅是匹配的正则表达式。
希望我没有产生另一个重复项,我无法真正总结问题陈述
答案1
awk -F_ '{print $(NF-1), $0}' | sort -k1,1 -n | cut -d' ' -f2-
这使用awk
with_
作为字段分隔符来复制倒数第二个字段 (约会时间) 到行的开头,然后用于sort
仅在该字段上对输入进行数字排序,然后cut
删除额外的字段。
示例输出将示例输入保存到名为的文件中file
:
$ awk -F_ '{print $(NF-1), $0}' file | sort -k1,1 -n | cut -d' ' -f2-
swid_state_definition_user_20171227162839_6515.log
package_user_20171227162858_7082.log
swid_appsrv_stop_user_20171227172258_27116.log
swid_state_definition_user_20171227172344_322.log
package_user_20171227172610_16198.log
swid_ds_install_user_20171227172654_20425.log
swid_ds_install_user_20171227172732_23839.log
package_user_20171227172949_5627.log
package_user_20171227233634_23845.log
这假设日期时间始终位于倒数第二个字段中。如果那是不是在这种情况下,如果您使用 GNU awk,您可以捕获以下模式:看起来像日期时间一样,并将其添加到行的开头:
$ awk -F_ '{match($0,"_(20[0-9]{12})_",dt); print dt[1], $0}' file |
sort -k1,1 -n | cut -d' ' -f2-
但我倾向于perl
在这种情况下使用。
GNU awk 的match()
函数采用可选的第三个参数,即用于存储任何捕获的匹配项的数组变量的名称。在这种情况下,只会有一个捕获,因此它将存储在数组的第一个元素中,例如dt[1]
。 IIRC,POSIX awk 仍然没有任何方法来捕获正则表达式匹配。
顺便说一句,现在的假设是年份 >= 2000。如果您的输入数据并不总是如此,请调整正则表达式以适应。
答案2
您可以zsh
在这里使用 glob,例如:
printf '%s\n' *_user_*.log(oe:'REPLY=${REPLY##*user_}':)
whereoe:...:
定义基于给定表达式的排序顺序。在这里,我们选择“user_”右侧的文件名部分。
对最后 2 个进行排序_*
:
printf '%s\n' *_*_*.log(oe:'REPLY=${(M)REPLY%_*_*}':)
答案3
这看起来应该有效:
$ perl -e 'sub key($) { $_[0] =~ /(\d+)_\d+\.log$/; return $1; };
@lines = <>; print sort {key($a) cmp key($b)} @lines;' < files
swid_state_definition_user_20171227162839_6515.log
package_user_20171227162858_7082.log
swid_appsrv_stop_user_20171227172258_27116.log
swid_state_definition_user_20171227172344_322.log
package_user_20171227172610_16198.log
swid_ds_install_user_20171227172654_20425.log
swid_ds_install_user_20171227172732_23839.log
package_user_20171227172949_5627.log
package_user_20171227233634_23845.log
该子例程key
根据以下事实选择数字字符串:日期时间似乎始终是文件名的倒数第二部分,位于.log
和其他数字字段之前。然后我们读入输入行,并使用 的输出key()
作为排序键排序打印它们。
Perl可以采用内联代码块来获取要与和sort
进行比较的值,并返回小于、等于或大于(作为字符串进行比较)。$a
$b
cmp
如果时间戳的位置实际上可能变化更大,我们可以更改 sub 以在字符串中的任何位置选择 14 位数字的字符串,例如,在此处用下划线分隔:
sub key($) { $_[0] =~ /_(\d{14})_/; return $1; }