我想检查一个字符串是否多次包含一个字母(不是特定字母,实际上是任何字母)。
例如:
用户:
test.sh this list
脚本:
if [ "$1" has some letter more then once ]
then
do something
fi
答案1
您可以使用grep
。
正则表达式\(.\).*\1
匹配任何单个字符,后跟任意字符,后跟相同的第一个字符。
grep
如果至少一行与正则表达式匹配,则返回成功。
if echo "$1" | grep -q '\(.\).*\1' ; then
echo "match" ;
fi
请注意,\(.\)
匹配任何字符而不是任何字母,也许您必须将正则表达式限制为“的特定定义”真的任何字母“。您可以使用诸如\([[:alnum:]]\).*\1
,\([[:alpha:]]\).*\1
或 之类的东西\([a-df-z1245]\).*\1
。
答案2
您可以使用fold
每行打印一个字符的字符串,然后uniq -c
对它们进行计数并awk
仅打印那些出现多次的字符串:
$ string="foobar"
$ fold -w 1 <<< "$string" | sort | uniq -c | awk '$1>1'
2 o
或者,如果你的 shell 不支持这里的字符串:
printf '%s\n' "$string" | fold -w 1 | sort | uniq -c | awk '$1>1'
然后,您可以测试上面的命令是否返回空字符串:
$ string="foobar"
$ [ -n "$(fold -w 1 <<<"$string" | sort | uniq -c | awk '$1>1')" ] && echo repeated
repeated
然后,您可以轻松扩展它以打印重复的字符及其重复的次数:
$ rep="$(fold -w 1 <<<"$string" | sort | uniq -c | awk '$1>1')"
$ [ -n "$rep" ] && printf -- "%s\n" "$rep"
2 o
答案3
c=$(expr " $string" : " .*\(.\).*\1") || [ "$c" = 0 ] &&
printf '"%s" has "%s" (at least) more than once\n' "$string" "${c:-<newline>}"
(0 返回expr
false,换行符命令替换条必须特殊处理)。
获取重复报告字节,在 GNU 系统上,你可以这样做:
$ string=$'This is a string\nwith «multi-byte» «characters»\n'
printf %s "$string" | od -An -vtc -w1 | LC_ALL=C sort | LC_ALL=C uniq -dc
5
3 a
2 c
2 e
3 h
5 i
3 r
4 s
5 t
2 \n
2 253
2 273
4 302
ASCII 覆盖范围之外的字节以其八进制值表示,控制字符以其八进制值或\x
C 表示形式表示。
获取重复报告人物:
$ printf %s "$string" | recode ..dump | sort | uniq -dc
2 000A LF line feed (lf)
5 0020 SP space
3 0061 a latin small letter a
2 0063 c latin small letter c
2 0065 e latin small letter e
3 0068 h latin small letter h
5 0069 i latin small letter i
3 0072 r latin small letter r
4 0073 s latin small letter s
5 0074 t latin small letter t
2 00AB << left-pointing double angle quotation mark
2 00BB >> right-pointing double angle quotation mark
但请注意,它recode
并不知道所有 Unicode 字符(尤其是最近的字符)。
使用 shell 内置函数。
在 ksh93 中:
if [[ $string = *@(?)*\1* ]]; then
print -r -- "$string contains duplicate characters"
fi
在 zsh 中:
set -o rematchpcre
if [[ $string =~ '(.).*\1' ]]; then
print -r -- "$string contains duplicate characters ($match[1] at least)"
fi
(也可以在没有set -o rematchpcre
但仅在 ERE 支持反向引用作为标准扩展的系统上工作)。
或者获取所有重复字符的列表:
typeset -A count=()
for c (${(s[])string}) if (( ++count[\$c] == 2 )) print -r -- $c is found more than once
答案4
即使这个问题已经在 8 年前被问过,考虑到之前的所有答案都需要外部工具,长管道表达式需要多个子 shell,尽管问题被标记为 bash,我还是想提出一个内部解决方案。
该函数的count_chars()
工作原理与同名的 PHP 函数类似。它接受一个字符串作为输入,并为每个字符记录其在关联数组中出现的次数。保存结果的数组作为第一个参数通过引用传递。
然后通过循环索引(键)就可以轻松获取所有满足过滤条件的字符。
编辑:更新后的代码应该适用于 Bash 4.3 及更高版本。
#!/bin/bash
# Count character occurences in string $2. For each contained character, return
# the number of occurrences in the associative array $1.
# This is similar to the PHP function count_chars(), mode 1.
count_chars() {
[ "$1" = "arr" ] || { declare -n arr 2>/dev/null || return 1; arr="$1"; }
arr=( )
local -i i
local ch
for (( i=0; i<${#2}; i++ )); do
ch=${2:$i:1}
# http://mywiki.wooledge.org/BashPitfalls#A.5B.5B_-v_hash.5B.24key.5D_.5D.5D
[[ -v 'arr["$ch"]' ]] || arr["$ch"]="0"
# Surprise, surpise--the increment works, despite
# http://mywiki.wooledge.org/BashPitfalls#A.28.28_hash.5B.24key.5D.2B-.2B-_.29.29
# (( ++arr["$ch"] )) EDIT: Bash 5.2+ only
let '++arr["$ch"]'
done
}
declare -A A=
count_chars A "Die Hoffnung stirbt zuletzt!"
for k in "${!A[@]}"; do
(( ${A[$k]} > 1 )) && printf '%s|' "$k"
done
echo
该脚本将打印出:
|z|u|t|n|i|f|e|
第一个结果字符是空白。您可以轻松验证这是正确的:
$ declare -p A
declare -A A=(["!"]="1" [" "]="3" [H]="1" [D]="1" [z]="2" [u]="2" [t]="4" [s]="1" [r]="1" [o]="1" [n]="2" [l]="1" [i]="2" [g]="1" [f]="2" [e]="2" [b]="1" )
如果您希望继续处理数组,可以从数组中删除不匹配的元素:
for k in "${!A[@]}"; do
(( ${A[$k]} > 1 )) || unset -v 'A[$k]'
done
declare -p A
结果:
declare -A A=([" "]="3" [z]="2" [u]="2" [t]="4" [n]="2" [i]="2" [f]="2" [e]="2" )