使用 linux 命令对第二列进行排序
Sort second column using linux command
是否可以水平排序文本?
例如我有这个 hunspell 文件,其中包含所有英文单词后跟标签。 (它可能包含 unicode 文本和数百万个单词)
test/BACac
this/QPR
line/MNP
again/Xx
我需要对标签进行排序(最好是:先小写再大写)
预期:
test/acABC
this/PQR
line/MNP
again/xX
我可以在 pandas 中做到这一点。但我想知道我是否可以仅使用 linux 个命令来完成任务!
import pandas as pd
df = pd.read_csv('test.csv', sep='/', header=None)
df.columns = ['word', 'tags']
df['tags']=df['tags'].map(lambda x: ''.join(sorted([i for i in x])))
df['final'] = df['word'] + '/' + df['tags']
df['final'].to_csv('result.csv', index=False, header=None)
这可能对你有用(GNU sed 和排序):
sed -E 's#/([[:upper:]]*)(.*)#/#' file | sort -ft/ -k2,2
交换第二个字段中的大小写字母,然后不管大小写对第二个字段中的结果进行排序。
如果大小写字母缠绕在一起,使用:
sed -E ':a;s#/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#/#;ta' file |
sort -ft/ -k2,2
我误解了问题:
sed -E ':a;s#/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#/#;ta' file |
sed -zE 's#/([[:lower:]]*)(.*)#/\n\n#mg' |
sed '2~3,+1s/.*/echo "&" | sed -z "s#\B#\n#g" | sort | sed -z "s#\n##g"/e' |
sed 'N;N;s/\n//g'
将 /
后面的大写字母和小写字母分开,然后将小写字母放在前面。
将每一行分成 3 行记录,第一行是第一个字段,第二行和第三行分别是第二个字段的小写字母和大写字母。
对每第二行和第三行进行排序,将每一行的每个字母分成一行。然后对生成的行集进行排序,并将行内的行集重新构造回单行。
替代方案,也许更好?:
sed -zE 's/(.*\/)(.*)/\n/mg' file |
sed -E 'N;s/(.*)\n(.*)/echo ""|sed -z "s#\B#\n#g"|sort|sed -z "s#\n##g"|sed "s#^##"/e' |
sed -E ':a;s/\/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)/\//;ta'
当然,有一个实用程序可以执行其中的一些操作:
sed -zE 's/(.*\/)(.*)/\n/mg' file |
sed -E 'N;s/(.*)\n(.*)/echo ""|fold -b1|sort|tr -d "\n"|sed "s#^##"/e' |
sed -E ':a;s/\/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)/\//;ta
事实上,解决方案可以作为一个替换在一行中呈现:
sed -E 's/^(.*\/)(.*)/echo ""|fold -b1|sort|tr -d "\n"|sed -E ":a;s#^([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#\1\3\2#;ta;s#^##"/e' file
这在 awk 中有点尴尬。但有时最好的 awk 确实是 perl:
perl -F/ -lane 'printf "%s/%s\n", $F[0], join "", sort split //, $F[1];'
或
perl -F/ -lape '$_ = $F[0] . "/". join "", sort split //, $F[1];'
或
perl -lape 's@(?=/)(.*)@join "", sort split //, @e'
以上都是同一个原理,不过最后的解决方案还是需要说明一下的。 (?=/)
是一个否定的先行断言,因此表达式 (?=/)(.*)
匹配行中第一个 /
之后的所有文本,但不消耗 /
。 /
之后的所有字符都放在第一个匹配组中,以便sort split
对其进行操作。 split //,
将匹配组拆分为单独的字符,这些字符传递给 sort
,然后通过连接重新加入,没有分隔符。 join/sort/split
的结果用作匹配模式的替换。
使用 GNU awk 处理“sorted_in”并在指定空分隔符时将字符串拆分为字符:
$ cat tst.awk
BEGIN {
FS=OFS="/"
PROCINFO["sorted_in"] = "@val_str_asc"
}
{
split(,lets,"")
= ""
for (i in lets) {
= lets[i]
}
print
}
$ awk -f tst.awk file
test/ABCac
this/PQR
line/MNP
again/Xx
要获得小写字母排在大写字母之前的输出,您必须找到具有这种整理顺序的语言环境,并在 运行 脚本之前设置 LC_ALL=<that locale>
或将所有大写字母转换为首先是小写字母,反之亦然,然后进行排序,然后在打印之前将它们转换回来,或者通过在每个真实字符前面放置一个装饰字符来做类似的事情,例如所有小写字母都得到前导 A
而大写得到前导 a
再次强制执行不同的顺序,例如:
$ cat tst.awk
BEGIN {
FS=OFS="/"
PROCINFO["sorted_in"] = "@val_str_asc"
}
{
split(,lets,"")
for (i in lets) {
lets[i] = ( lets[i] ~ /[[:lower:]]/ ? "A" : "a" ) lets[i]
}
= ""
for (i in lets) {
= substr(lets[i],2)
}
print
}
$ awk -f tst.awk file
test/acABC
this/PQR
line/MNP
again/xX
这是 perl
的替代解决方案,它首先给出小写字母:
$ perl -F'/' -lane '$s = join "", sort split //, $F[1];
print $F[0], "/", $s =~ s/^([A-Z]++)(.+)//r' ip.txt
test/acABC
this/PQR
line/MNP
again/xX
另一种选择:
$ perl -pe 's|.*/\K.+|join("", sort split //, $&) =~ s/^([A-Z]++)(.+)//r|e' ip.txt
test/acABC
this/PQR
line/MNP
again/xX
另一个 GNU sed 替代品:
parse.sed
# Save line to hold-space
h
# Remove word
s:.*/::
# New-line separate letters
s/./&\n/g
s/\n$//
# Quote new-line separated string
s/^|$/'/g
# Sort the letters and remove new-lines
s/^/echo /
s/$/ | sort/e
s/\n//g
# Move capital letters to the end (thanks @potong)
:a
s/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)//
ta
# Recombine word and tag
G
s:/.*::
s:([^\n]*)\n(.*):/:
运行 像这样:
sed -Ef parse.sed infile
输出:
test/acABC
this/PQR
line/MNP
again/xX
是否可以水平排序文本? 例如我有这个 hunspell 文件,其中包含所有英文单词后跟标签。 (它可能包含 unicode 文本和数百万个单词)
test/BACac
this/QPR
line/MNP
again/Xx
我需要对标签进行排序(最好是:先小写再大写) 预期:
test/acABC
this/PQR
line/MNP
again/xX
我可以在 pandas 中做到这一点。但我想知道我是否可以仅使用 linux 个命令来完成任务!
import pandas as pd
df = pd.read_csv('test.csv', sep='/', header=None)
df.columns = ['word', 'tags']
df['tags']=df['tags'].map(lambda x: ''.join(sorted([i for i in x])))
df['final'] = df['word'] + '/' + df['tags']
df['final'].to_csv('result.csv', index=False, header=None)
这可能对你有用(GNU sed 和排序):
sed -E 's#/([[:upper:]]*)(.*)#/#' file | sort -ft/ -k2,2
交换第二个字段中的大小写字母,然后不管大小写对第二个字段中的结果进行排序。
如果大小写字母缠绕在一起,使用:
sed -E ':a;s#/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#/#;ta' file |
sort -ft/ -k2,2
我误解了问题:
sed -E ':a;s#/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#/#;ta' file |
sed -zE 's#/([[:lower:]]*)(.*)#/\n\n#mg' |
sed '2~3,+1s/.*/echo "&" | sed -z "s#\B#\n#g" | sort | sed -z "s#\n##g"/e' |
sed 'N;N;s/\n//g'
将 /
后面的大写字母和小写字母分开,然后将小写字母放在前面。
将每一行分成 3 行记录,第一行是第一个字段,第二行和第三行分别是第二个字段的小写字母和大写字母。
对每第二行和第三行进行排序,将每一行的每个字母分成一行。然后对生成的行集进行排序,并将行内的行集重新构造回单行。
替代方案,也许更好?:
sed -zE 's/(.*\/)(.*)/\n/mg' file |
sed -E 'N;s/(.*)\n(.*)/echo ""|sed -z "s#\B#\n#g"|sort|sed -z "s#\n##g"|sed "s#^##"/e' |
sed -E ':a;s/\/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)/\//;ta'
当然,有一个实用程序可以执行其中的一些操作:
sed -zE 's/(.*\/)(.*)/\n/mg' file |
sed -E 'N;s/(.*)\n(.*)/echo ""|fold -b1|sort|tr -d "\n"|sed "s#^##"/e' |
sed -E ':a;s/\/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)/\//;ta
事实上,解决方案可以作为一个替换在一行中呈现:
sed -E 's/^(.*\/)(.*)/echo ""|fold -b1|sort|tr -d "\n"|sed -E ":a;s#^([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)#\1\3\2#;ta;s#^##"/e' file
这在 awk 中有点尴尬。但有时最好的 awk 确实是 perl:
perl -F/ -lane 'printf "%s/%s\n", $F[0], join "", sort split //, $F[1];'
或
perl -F/ -lape '$_ = $F[0] . "/". join "", sort split //, $F[1];'
或
perl -lape 's@(?=/)(.*)@join "", sort split //, @e'
以上都是同一个原理,不过最后的解决方案还是需要说明一下的。 (?=/)
是一个否定的先行断言,因此表达式 (?=/)(.*)
匹配行中第一个 /
之后的所有文本,但不消耗 /
。 /
之后的所有字符都放在第一个匹配组中,以便sort split
对其进行操作。 split //,
将匹配组拆分为单独的字符,这些字符传递给 sort
,然后通过连接重新加入,没有分隔符。 join/sort/split
的结果用作匹配模式的替换。
使用 GNU awk 处理“sorted_in”并在指定空分隔符时将字符串拆分为字符:
$ cat tst.awk
BEGIN {
FS=OFS="/"
PROCINFO["sorted_in"] = "@val_str_asc"
}
{
split(,lets,"")
= ""
for (i in lets) {
= lets[i]
}
print
}
$ awk -f tst.awk file
test/ABCac
this/PQR
line/MNP
again/Xx
要获得小写字母排在大写字母之前的输出,您必须找到具有这种整理顺序的语言环境,并在 运行 脚本之前设置 LC_ALL=<that locale>
或将所有大写字母转换为首先是小写字母,反之亦然,然后进行排序,然后在打印之前将它们转换回来,或者通过在每个真实字符前面放置一个装饰字符来做类似的事情,例如所有小写字母都得到前导 A
而大写得到前导 a
再次强制执行不同的顺序,例如:
$ cat tst.awk
BEGIN {
FS=OFS="/"
PROCINFO["sorted_in"] = "@val_str_asc"
}
{
split(,lets,"")
for (i in lets) {
lets[i] = ( lets[i] ~ /[[:lower:]]/ ? "A" : "a" ) lets[i]
}
= ""
for (i in lets) {
= substr(lets[i],2)
}
print
}
$ awk -f tst.awk file
test/acABC
this/PQR
line/MNP
again/xX
这是 perl
的替代解决方案,它首先给出小写字母:
$ perl -F'/' -lane '$s = join "", sort split //, $F[1];
print $F[0], "/", $s =~ s/^([A-Z]++)(.+)//r' ip.txt
test/acABC
this/PQR
line/MNP
again/xX
另一种选择:
$ perl -pe 's|.*/\K.+|join("", sort split //, $&) =~ s/^([A-Z]++)(.+)//r|e' ip.txt
test/acABC
this/PQR
line/MNP
again/xX
另一个 GNU sed 替代品:
parse.sed
# Save line to hold-space
h
# Remove word
s:.*/::
# New-line separate letters
s/./&\n/g
s/\n$//
# Quote new-line separated string
s/^|$/'/g
# Sort the letters and remove new-lines
s/^/echo /
s/$/ | sort/e
s/\n//g
# Move capital letters to the end (thanks @potong)
:a
s/([[:lower:]]*)([[:upper:]]+)([[:lower:]]+)//
ta
# Recombine word and tag
G
s:/.*::
s:([^\n]*)\n(.*):/:
运行 像这样:
sed -Ef parse.sed infile
输出:
test/acABC
this/PQR
line/MNP
again/xX