文字列に特定の文字が複数回含まれていることを確認する

文字列に特定の文字が複数回含まれていることを確認する

文字列に文字(特定の文字ではなく実際にはすべての文字)が複数回含まれていることを確認したいと思います。

たとえば、

ユーザー:

test.sh this list

スクリプト:

if [ "$1" has some letter more then once ]
then 
do something
fi

答え1

あなたはそれを使用することができますgrep

正規表現は、\(.\).*\1ランダムな単一文字、その後にランダムな文字、その後に同じ最初の文字が続くものと一致します。

grep1 つ以上の行が正規表現と一致する場合、成功を返します。

if echo "$1" | grep -q '\(.\).*\1' ; then  
  echo "match" ; 
fi

文字以外のすべての文字と一致させるには、正規表現を「」の特定の定義\(.\)に制限する必要があります。本当にどんな手紙でも" . または同じものを使用できます\([[:alnum:]]\).*\1\([[:alpha:]]\).*\1\([a-df-z1245]\).*\1

答え2

fold1行に1文字を印刷する文字列を使用してから、そのuniq -c数を数えてawk複数回表示される文字のみを印刷できます。

$ string="foobar"
$ fold -w 1 <<< "$string" | sort | uniq -c | awk '$1>1'
      2 o

またはシェルがこの文字列をサポートしていない場合:

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範囲外のバイトは8進数値で表され、制御文字は\x8進値または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文字(特に最近の文字)はわかりません。


シェル組み込み関数を使用します。

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年前に提起された質問ですが、以前のすべての回答には外部ツールが必要で、複数のサブシェルを必要とする長いパイプ表現が必要であることを考慮して、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" )

関連情報