检查字符串是否多次包含某个字符

检查字符串是否多次包含某个字符

我想检查一个字符串是否多次包含一个字母(不是特定字母,实际上是任何字母)。

例如:

用户:

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 返回exprfalse,换行符命令替换条必须特殊处理)。

获取重复报告字节,在 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 覆盖范围之外的字节以其八进制值表示,控制字符以其八进制值或\xC 表示形式表示。

获取重复报告人物:

$ 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" )

相关内容