我正在尝试按照单词在文件中出现的顺序对单词文件进行排序(我只对文件中的某些单词感兴趣)。第一个单词显示在输出的顶部,最后一个单词显示在底部。
生成字数统计的常用方法,使用 sort | uniq -c
,消除了排序顺序。如何在不丢失排序的情况下生成此频率计数?
示例文本文件:
戈达尔意识 蒜泥蛋黄酱 Ouija 风神 胜利 愤怒 完美 家庭 十二 银 七 混杂 放射性 一个 你 星期四 心 吃 披萨 传染 附近 公主 离子 水 王牌 火成岩 野心勃勃
示例输出:
1 conscious
1 aioli
1 Ouija
1 Aeolus
1 victorious
1 furious
1 promiscuous
1 radioactive
1 contagious
1 igneous
1 ambitious
awk
救援!
双重扫描以获取计数
$ awk -v RS=' +|n' 'NR==FNR {t=$0; if(gsub(/[aeiou]/,"")>2) a[t]++; next}
$0 in a {print a[$0],$0; delete a[$0]}' file{,}
1 conscious
1 aioli
1 Ouija
1 Aeolus
1 victorious
1 furious
1 promiscuous
1 radioactive
1 contagious
1 igneous
1 ambitious
从排序列表中提取一些其他方法,这将基于输入排序生成计数
$ awk -v RS=' +|n' '{t=$0} gsub(/[aeiou]/,"")>2{print t}' file |
# or some other means to generate filtered words ...
cat -n | # add line number
sort -k2 -k1n | # sort by words and line number
uniq -f1 -c | # find counts skipping line number
sort -k2n | # sort by original line number
awk '{print $1,$3}' # remove the line number
第一部分:提取匹配的单词
以下命令:
s='Godard conscious aioli Ouija Aeolus victorious furious perfect family twelve silver seven promiscuous radioactive one you Thursday heart Ate pizza contagious near princess ion water ace igneous ambitious'
tr '[[:space:]]' 'n' <<<"$s" | egrep -i '[aeoiu].*[aeiou].*[aeiou]'
。生成输出:
conscious
aioli
Ouija
Aeolus
victorious
furious
promiscuous
radioactive
contagious
igneous
ambitious
。它正确包含至少具有三个元音的单词子集,按其原始出现顺序排列。
第二部分:在保持排序顺序的同时添加计数器
维护计数器需要维护状态或执行多次传递。
#!/usr/bin/env bash
if [[ -z $BASH_VERSION ]] || [[ $BASH_VERSION = [1-3].* ]]; then
echo "ERROR: This requires bash 4.0 or newer" >&2
exit 1
fi
### Begin code from Part 1
s='Godard conscious aioli Ouija Aeolus victorious furious perfect family twelve silver seven promiscuous radioactive one you Thursday heart Ate pizza contagious near princess ion water ace igneous ambitious'
get_words() { tr '[[:space:]]' 'n' <<<"$s" | egrep -i '[aeoiu].*[aeiou].*[aeiou]'; }
### End code from Part 1
declare -a var_order=( )
declare -A var_count=( )
while IFS= read -r var; do
if (( ${var_count[$var]} )); then
var_count[$var]=$(( ${var_count[$var]} + 1 ))
else
var_order+=( "$var" )
var_count[$var]=1
fi
done < <(get_words)
for var in "${var_order[@]}"; do
printf '% -4d %sn' "${var_count[$var]}" "$var"
done
。正确生成输出:
1 conscious
1 aioli
1 Ouija
1 Aeolus
1 victorious
1 furious
1 promiscuous
1 radioactive
1 contagious
1 igneous
1 ambitious
我也应该参与其中。
这是一句话,只给查尔斯:
gawk -v RS="[[:space:]]+" '{$0=tolower($0)} /[aeiou]{3}/ && !($0 in p) {p[$0]=n++} /[aeiou]{3}/ {a[p[$0]]=$0;c[p[$0]]++} END { for (i=0;i<n;i++) printf "%6d %sn",c[i],a[i] }' input.txt
为了便于阅读(和评论):
#!/usr/bin/env gawk -f
BEGIN {
RS="[[:space:]]+" # Set a reasonable record separator
} # (includes spaces and newlines)
{
$0=tolower($0) # ignore case...
}
/[aeiou]{3}/ && !($0 in p) { # if we've found a word, make sure
p[$0]=n++ # we have a pointer to it.
}
/[aeiou]{3}/ { # if we've found a word and have a pointer,
a[p[$0]]=$0 # make a record of the word,
c[p[$0]]++ # and increment its counter.
}
END { # Once everything's been processed,
for (i=0;i<n;i++) # step through our list, and
printf "%6d %sn",c[i],a[i] # print the results.
}
这涵盖了多种形式的空格,准确计数并保持单词有序。哦,它一次通过即可完成此操作。
在简单的抨击中,你可以做到:
set -f
shopt -s nocasematch
for word in $(< words.txt); do
[[ $word == *[aeiou][aeiou][aeiou]* ]] && echo $word
done
那只是打印出带有 3 个连续元音的单词,它不计算它们。
考虑更可能的输入
cat txt1
戈达尔意识 蒜泥蛋黄酱 奥伊贾 风神 胜利 愤怒 完美家庭 十二银七混杂放射性一你星期四野心勃勃 心吃披萨传染附近公主离子水王牌野心勃勃 火成岩野心勃勃意识
下面awk
脚本可以解决问题:
awk 'NR==FNR {v[i++]=$0;c[$0]++;next}END{
for(j=0;j<i;j++){if(p[v[j]]==0){print c[v[j]],v[j]}
p[v[j]]=c[v[j]]>1?1:0;}
}' <(awk -v RS=' +|n' '$0 ~ /(.*[aAeEiIoOuU].*){3}/' txt1)
输出
2 conscious
1 aioli
1 Ouija
1 Aeolus
1 victorious
1 furious
1 promiscuous
1 radioactive
3 ambitious
1 contagious
1 igneous