文字列に文字(特定の文字ではなく実際にはすべての文字)が複数回含まれていることを確認したいと思います。
たとえば、
ユーザー:
test.sh this list
スクリプト:
if [ "$1" has some letter more then once ]
then
do something
fi
答え1
あなたはそれを使用することができますgrep
。
正規表現は、\(.\).*\1
ランダムな単一文字、その後にランダムな文字、その後に同じ最初の文字が続くものと一致します。
grep
1 つ以上の行が正規表現と一致する場合、成功を返します。
if echo "$1" | grep -q '\(.\).*\1' ; then
echo "match" ;
fi
文字以外のすべての文字と一致させるには、正規表現を「」の特定の定義\(.\)
に制限する必要があります。本当にどんな手紙でも" . または同じものを使用できます\([[:alnum:]]\).*\1
。\([[:alpha:]]\).*\1
\([a-df-z1245]\).*\1
答え2
fold
1行に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は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範囲外のバイトは8進数値で表され、制御文字は\x
8進値または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" )