根据记录的日期时间部分对列表进行排序,其中记录中的日期时间位置可能会有所不同

根据记录的日期时间部分对列表进行排序,其中记录中的日期时间位置可能会有所不同

我想根据列表名称的日期时间部分对其进行排序。

使用排序可以吗?我无法指定排序列,因为该列可能会有所不同,如下面的示例输入所示。

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-

这使用awkwith_作为字段分隔符来复制倒数第二个字段 (约会时间) 到行的开头,然后用于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$bcmp

如果时间戳的位置实际上可能变化更大,我们可以更改 sub 以在字符串中的任何位置选择 14 位数字的字符串,例如,在此处用下划线分隔:

sub key($) { $_[0] =~ /_(\d{14})_/; return $1; }

相关内容