我有一个文本文件,每行都有一个单词。我正在尝试删除至少没有两个不同字母的行。例如,该文件看起来像这样:
words
books
aaa
letters
zzzz
我希望输出文件如下所示:
words
books
letters
我尝试将每个单词分解为单独的字母,而不是使用uniq -c
then将它们组合在一起wc -l
,但陷入了 if 语句。还相信一定有一种更简单的方法来做到这一点,我只是想不出任何其他方法来解决这个问题。
答案1
假设你的意思是特点而不是字母(例如,您还想删除包含...
或的行,11
即使.
或1
不是字母):
grep -vx -e '' -e '\(.\)\1*'
或者:
grep -vx '\(\(.\)\2*\)\{0,1\}'
即删除 ( -v
) 空行或以一个字符 ( .
) 开头的行,后跟同一字符(\1
对 捕获的内容的反向引用\(...\)
)重复 0 次或多次 ( *
) 直到行尾(-x
将模式锚定在行的开头和结尾)。
可移植的是,您不能在此处使用egrep
或 ,grep -E
因为标准 ERE 没有反向引用(只有 BRE 有)。
对于至少包含两个不同字母的行,忽略其他类型的字符(我们将[[:alpha:]]
在此处使用信,即被视为的任何字符按字母顺序排列的在您的语言环境中):
grep -vx '[^[:alpha:]]*
[^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\1\)*[^[:alpha:]]*'
(在两条线上,这是传递两种不同模式的另一种方式)。或者:
grep -vx '[^[:alpha:]]*\([^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\2\)*[^[:alpha:]]*\)\{0,1\}'
那个会删除像12345aaa
(只有一个字母)或-+-+-+-
(没有字母)这样的行。
如果您还想删除Aaaa
行(即比较字母时忽略大小写),请添加该-i
选项。
请注意,它在字符级别工作,因此如果存在用多个字符表示的字素,它可能不会执行您所期望的操作。例如,它将删除类似于以下输出的行:
$ printf 'e\u0300e\u0301\n'
èé
(假设 GNUprintf
或兼容),但不是这样的:
$ printf '\ue8\ue9\n'
èé
(其中e\u300
是字素的分解形式和\ue8
预组合形式è
;e
(U+0065) 和è
(U+00E8) 是按字母顺序排列的,但不是结合重音/尖音的 U+0300 或 U+0301)。
要使用字素,您可以使用pcregrep
或 GNUgrep
及其-P
选项:
对于第一种情况(至少两个不同的字素簇):
grep -vxP '(?:(\X)\1*)?'
对于第二种情况(至少有两个不同的信字素簇):
grep -vxP '(?:(?=\PL)\X)*(?:((?=\pL)\X)(?:(?:(?=\PL)\X)*\1(?!\pM))*(?:(?=\PL)\X)*)?'
其中(?=\PL)\X
是一个非字母字素簇(一个字素簇 ( \X
) ,前提是(?=...)
它以非字母 ( \PL
) 和(?=\pL)\X
字母字素簇开头。
\pL
匹配于信正确的unicode。与 POSIX 字符类相反[:alpha:]
,它还包括非字母脚本中的字母。
请注意,它会将e\u300\u301
, e\u301\u300
, \ue9\u300
,\ue8\u301
视为四个不同的簇,即使它们都是e
带有锐音和重音的 a。
还要注意像ffi
(U+FB03) 这样在一个字符中包含多个字母的字符特点。
通过PCRE,您还可以采取积极的方法:
至少 2 个不同的字符:
grep -P '(.).*(?!\1).'
至少 2 个不同的字母字符:
grep -P '(\pL).*(?!\1)\pL'
至少 2 个不同的字素簇:
grep -P '^\X*(\X)\X*(?!\1(?!\pM))\X'
(至少)分解形式的古兰经朝鲜文无法正常工作。 PCRE(
perl
与 的 RE相反\b{g}
)没有字素边界运算符 (AFAIK),并且对 unicode 属性的支持有限。我们使用(?!\pM)
(在这种情况下意味着:“只要它后面没有组合标记字符”)作为近似值,但这对于多部分朝鲜文不起作用字母/音节字符,其中部分没有那个财产。려련련
例如,它会删除。现在人们也可能会争辩说,每个部分是一个独特的信...对于
perl
5.22或更高版本,你可以这样写:perl -Mopen=locale -lne 'print if /\b{g}(\X).*\b{g}(?!\1\b{g})\X/'
至少2个不同的信字素簇:
grep -P '^\X*((?=\pL)\X)\X*(?!\1(?!\pM))(?=\pL)\X'
再次强调,不适用于
려련련
.和perl
:perl -Mopen=locale -lne 'print if /\b{g}(?=\pL)(\X).*\b{g}(?!\1\b{g})(?=\pL)\X/'
有了perl
,我们可以使用更直接的方法,例如:
至少 2 个不同的字符:
perl -Mopen=locale -MList::MoreUtils=uniq -lne ' print if uniq(/./g) >= 2'
至少 2 个不同的字母字符:
perl -Mopen=locale -MList::MoreUtils=uniq -lne ' print if uniq(/\pL/g) >= 2'
至少 2 个不同的字素簇:
perl -Mopen=locale -MList::MoreUtils=uniq -lne ' print if uniq(/\X/g) >= 2'
至少2个不同的信字素簇:
perl -Mopen=locale -MList::MoreUtils=uniq -lne ' print if uniq(grep /^\pL/, /\X/g) >= 2'
答案2
根据您的预期输出 - 您想要跳过超过 2 个单词完全相同的人物:
grep方法:
grep -vE '(.)(\1){2,}' file
输出:
words
books
letters
修改文件到位您可以应用以下内容sed方法:
sed -Ei '/(.)(\1){2}/d' file
答案3
查询的正向搜索:
while read -r line; do
n=$(echo "$line" | egrep -o . | sort -u);
[[ ${#n} -gt 1 ]] && echo "$line";
done < file