我有一个如下的文本文件:
info.txt
files-550519470 19h
files-1662192679 1d
files-247106034 1d
files-1986982365 2d
files-464153317 12m
files-739420408 3d
files-77614277 3m
files-374059185 4d
files-909323637 4d
files-101830442 5d
files-1270496134 5d
files-1797797160 6d
files-812888216 7d
files-118869238 7h
我想根据第二列字母进行排序,然后在同一第二列中按数字的降序排列,然后输出应如下所示:
files-812888216 7d
files-1797797160 6d
files-101830442 5d
files-101830442 5d
files-1270496134 5d
files-374059185 4d
files-909323637 4d
files-374059185 4d
files-909323637 4d
files-739420408 3d
files-1986982365 2d
files-1662192679 1d
files-247106034 1d
files-550519470 19h
files-118869238 7h
files-464153317 12m
files-77614277 3m
我可以通过下面的命令根据数字反转,但无法弄清楚字母。有人可以建议吗?
sort -r -nk2 info.txt
答案 0 :(得分:2)
使用Decorate, Sort, Undecorate模式:
$ sort -t $'-' -k 2 file |
sed -E 's/(.*) ([[:digit:]][[:digit:]]*)([dmh]$)/\2 \3 \1 \2\3/' |
awk 'BEGIN{arr["m"]=1; arr["h"]=60; arr["d"]=60*24}
{$2=$1*arr[$2]; $1=""; print}' |
sort -s -k1nr |
cut -d' ' -f3-
files-812888216 7d
files-1797797160 6d
files-101830442 5d
files-101830442 5d
files-1270496134 5d
files-374059185 4d
files-374059185 4d
files-909323637 4d
files-909323637 4d
files-739420408 3d
files-1986982365 2d
files-1662192679 1d
files-247106034 1d
files-550519470 19h
files-118869238 7h
files-464153317 12m
files-77614277 3m
这应该比Bash循环快得多。如果您有gawk
来替换sort
和sed
如果您使用GNU或BSD排序,则可以按字母顺序d<h<m
进行使用,而不进行转换:
$ sed -E 's/([^-]*)-(.*) ([[:digit:]][[:digit:]]*)([dmh]$)/\2 \4 \3 \1-\2 \3\4/' file |
sort -s -t $' ' -k2,2 -k3,3nr -k1,1 |
cut -d $' ' -f4-
# same output
答案 1 :(得分:1)
@edit
感谢@shelter的帮助!我们可以做到:
sed 's/\(.*\) \([0-9]*\)\([a-zA-Z]*\)/\3 \2 \1 \2\3/' |
sort -k1 -k2nr |
cut -d' ' -f3-
sed
在前面添加两列,一列带有第三列的字母,第二列带有第三列的数字我保留旧答案作为参考。
这是我的想法,它可以工作,但绝对不是最好的:
sed 's/\(.*\) \([0-9]*\)\([a-zA-Z]*\)/\3 \2 \1 \2\3/' |
sort -k1 |
{
presuffix=''
buff=''
while IFS=' ' read -r suffix rest; do
if [ "$presuffix" != "$suffix" ]; then
echo -n "$buff" | sort -n -r -k1
presuffix=$suffix
buff=''
fi
buff+="$rest"$'\n'
done
printf "%s" "$buff" | sort -n -r -k1
} |
cut -d' ' -f2-
1d
,因此该行以d 1 ... rest of the line
开头。因此,该行前面有两列新列-一列我们要按字母顺序排序,另一列我们要按数字排序。while read
中删除,因此现在是第一列)。cut -d' ' -f2-
删除了第一列(数字)。while read
部分的原因,这会很慢,但是我没有更好的主意。@edit:
确实受@shelter评论影响的另一种解决方案。
sed 's/\(.*\) \([0-9]*\)\([a-zA-Z]*\)/\3 \2 \1 \2\3/' |
while IFS=' ' read -r suffix num rest; do
echo "$(printf "%d * 256 + (256 - %d)\n" "'$suffix" "$num" | bc)" "$rest"
done |
sort -r -n |
cut -d' ' -f2-
假设排序列中只有一个字符后缀(1d
或1e
或1h
或19d
)并且排序列中的数字小于256(幻数,可能会增加),我们可以将字符转换为ascii数。
然后,我们可以将ASCII数乘以256,然后将其添加到已排序的列中。该数字减去256,因为我们希望在每个块中使用数字进行反向排序(7d
首先,然后是1d
)。然后,我们仅对其进行数字排序。
我们可以选择使用printf "(256 - %d) + %d"
然后进行逆序数字排序,不同之处仅在于两个字段相等时(例如files-1662192679
和files-247106034
的情况)。
幻数256
应该大于排序列中最大的数字,也应该大于排序列中字符的最大ascii表示。可能可以将其扩展为处理已排序的列中的多个字符。