如何对包含以下内容的文件进行排序? (s=秒,h=小时,d=天 m=分钟)
1s
2s
1h
2h
1m
2m
2s
1d
1m
答案1
awk '{ unitvalue=$1; };
/s/ { m=1 }; /m/ { m=60 }; /h/ { m=3600 }; /d/ { m=86400 };
{ sub("[smhd]","",unitvalue); unitvalue=unitvalue*m;
print unitvalue " " $1; }' input |
sort -n | awk '{ print $2 }'
1s
2s
2s
1m
1m
2m
1h
2h
1d
答案2
这是一个扩展MiniMax 的回答可以处理更广泛的持续时间值,例如1d3h10m40s
.
parse-times.awk
GNU Awk 程序(为了这个答案而存储):
#!/usr/bin/gawk -f
BEGIN{
FPAT = "[0-9]+[dhms]";
duration["s"] = 1;
duration["m"] = 60;
duration["h"] = duration["m"] * 60;
duration["d"] = duration["h"] * 24;
}
{
t=0;
for (i=1; i<=NF; i++)
t += $i * duration[substr($i, length($i))];
print(t, $0);
}
调用:
gawk -f parse-times.awk input.txt | sort -n -k 1,1 | cut -d ' ' -f 2
多列输入
如果您的输入包含多列,您将不得不诉诸其他方法。以下代码使用函数来解析时间表达式并将结果附加为新列:
#!/usr/bin/gawk -f
# Given a time expression like "1d3h10m40s", returns its duration in seconds.
# If the expression doesn't match, returns -1.
function parse_time(s) {
return match(s, /^(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?$/, m) ? m[10] + m[8] * 60 + m[6] * 3600 + m[4] * 86400 + m[2] * 604800 : -1;
}
{
$(++NF) = parse_time($1);
print;
}
然后,您可以对新列的输出进行排序,然后将其删除:
gawk -f parse-times.awk input.txt | sort -n -k 2,2 | cut -d ' ' -f 1
答案3
第一个版本 - 使用 FPAT
gawk '
BEGIN {
FPAT="[0-9]+|[smhd]";
}
/s/ { factor = 1 }
/m/ { factor = 60 }
/h/ { factor = 3600 }
/d/ { factor = 86400 }
{
print $1 * factor, $0;
}' input.txt | sort -n | awk '{print $2}'
FPAT - 描述记录中字段内容的正则表达式。设置后,gawk 将输入解析为字段,其中字段与正则表达式匹配,而不是使用FS变量作为字段分隔符。
第二版
我惊讶地发现,没有FPAT
它也能工作。这是由数字转换机制引起的awk
-awk 如何在字符串和数字之间进行转换,即:
通过将字符串的任何数字前缀解释为数字,将字符串转换为数字:“2.5”转换为 2.5,“1e3”转换为 1,000,“25fix”的数值为 25。 无法解释的字符串因为有效数字转换为零。
gawk '
/s/ { factor = 1 }
/m/ { factor = 60 }
/h/ { factor = 3600 }
/d/ { factor = 86400 }
{
print $0 * factor, $0;
}' input.txt | sort -n | awk '{print $2}'
输入(稍微改变)
1s
122s
1h
2h
1m
2m
2s
1d
1m
输出
笔记:122秒多了2分钟,所以排序在2m之后。
1s
2s
1m
1m
2m
122s
1h
2h
1d
答案4
Python 3 中的解决方案:
#!/usr/bin/python3
import re, fileinput
class RegexMatchIterator:
def __init__(self, regex, string, error_on_incomplete=False):
self.regex = regex
self.string = string
self.error_on_incomplete = error_on_incomplete
self.pos = 0
def __iter__(self):
return self
def __next__(self):
match = self.regex.match(self.string, self.pos)
if match is not None:
if match.end() > self.pos:
self.pos = match.end()
return match
else:
fmt = '{0!s} returns an empty match at position {1:d} for "{3!r}"'
elif self.error_on_incomplete and self.pos < len(self.string):
if isinstance(self.error_on_incomplete, str):
fmt = self.error_on_incomplete
else:
fmt = '{0!s} didn\'t match the suffix {3!r} at position {1:d} of {2!r}'
else:
raise StopIteration(self.pos)
raise ValueError(fmt.format(
self.regex, self.pos, self.string, self.string[self.pos:]))
DURATION_SUFFIXES = { 's': 1, 'm': 60, 'h': 3600, 'd': 24*3600 }
DURATION_PATTERN = re.compile(
'(\\d+)(' + '|'.join(map(re.escape, DURATION_SUFFIXES.keys())) + ')')
def parse_duration(s):
return sum(
int(m.group(1)) * DURATION_SUFFIXES[m.group(2)]
for m in RegexMatchIterator(DURATION_PATTERN, s,
'Illegal duration string {3!r} at position {1:d}'))
if __name__ == '__main__':
with fileinput.input() as f:
result = sorted((l.rstrip('\n') for l in f), key=parse_duration)
for item in result:
print(item)
正如你所看到的,我花了大约 ⅔ 的行数来构建一个有用的迭代器regex.match()
结果因为regex.finditer()
不会将匹配与当前区域的开头联系起来,并且没有其他合适的方法来迭代匹配结果。*咕噜*