문자열에 특정 문자가 여러 번 포함되어 있는지 확인

문자열에 특정 문자가 여러 번 포함되어 있는지 확인

문자열에 문자(특정 문자가 아니라 실제로는 모든 문자)가 두 번 이상 포함되어 있는지 확인하고 싶습니다.

예를 들어:

사용자:

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

또는 쉘이 이 문자열을 지원하지 않는 경우:

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모든 유니코드 문자(특히 최근 문자)를 알 수는 없습니다.


쉘 내장 함수를 사용하십시오.

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

관련 정보