Bashを使用して文字列内の部分文字列の出現回数を数えるにはどうすればよいですか?
例:
この部分文字列の回数を知りたい:
Bluetooth
Soft blocked: no
Hard blocked: no
...この文字列で発生します...
0: asus-wlan: Wireless LAN
Soft blocked: no
Hard blocked: no
1: asus-bluetooth: Bluetooth
Soft blocked: no
Hard blocked: no
2: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
113: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
注I: sed、grep、awkを使用していくつかの方法を試しました...スペースと複数行の文字列がある場合、何も機能しないようです。
注II:私はLinuxユーザーであり、通常はLinuxディストリビューションで見られるもの以外のアプリケーション/ツールのインストールを含まないソリューションを試しています。
重要:
以下の架空の例のようなものをお願いします。この場合、2つのShell変数(Bash)を使用します。
例:
STRING="0: asus-wlan: Wireless LAN
Soft blocked: no
Hard blocked: no
1: asus-bluetooth: Bluetooth
Soft blocked: no
Hard blocked: no
2: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
113: hci0: Bluetooth
Soft blocked: no
Hard blocked: no"
SUB_STRING="Bluetooth
Soft blocked: no
Hard blocked: no"
awk -v RS='\0' 'NR==FNR{str=$0; next} {print gsub(str,"")}' "$STRING" "$SUB_STRING"
注:説明のためにawkを使用しています!
Perl
の場合:
printf '%s' "$SUB_STRING" |
Perl -l -0777 -ne '
BEGIN{$sub = <STDIN>}
@matches = m/\Q$sub\E/g;
print scalar @matches' <(printf '%s' "$STRING")
bash
だけで、常に次のようなことができます。
s=${STRING//"$SUB_STRING"}
echo "$(((${#STRING} - ${#s}) / ${#SUB_STRING}))"
つまり、$s
には$STRING
が含まれ、その中の$SUB_STRING
はすべて削除されます。 $SUB_STRING
と$STRING
の間の文字数の差を計算し、$s
自体の長さで除算することにより、削除された$SUB_STRING
の数を確認します。
POSIXly、あなたは次のようなことをすることができます:
s=$STRING count=0
until
t=${s#*"$SUB_STRING"}
[ "$t" = "$s" ]
do
count=$((count + 1))
s=$t
done
echo "$count"
文字列処理関数を使用して、Perlで次のように実行できます。
printf '%s\n' "$STRING" |
Perl -nse '
$_ .= join "", <>;
$k++ while ++($p = index($_, $s, $p));
print $k, "\n" ;
' -- -s="$SUB_STRING"
説明:
° load up the whole string in $_
° index function will return the position of a substring in a string OTW returns -1
° progressively match the substring and use the position found as the starting position for the next search.
° all this while increment the counter $k depicting substring found.
他のいくつかの方法を以下に示します。
文字列を丸呑みし、正規表現を使用します。
printf '%s\n' "$STRING" |
Perl -slp -0777e '
$_ = () = /$s/g;
' -- -s="$s"
°文字列を$ _変数に丸める。
°-sオプションを使用して、コマンドラインからPerlに部分文字列を渡します。
°$ _で一致を実行し、リストコンテキストで一致を取得し、スカラーコンテキストで一致の数を取得します。
°-pオプションは$ _の内容を自動印刷します。
Sedツールを使用する方法:
esc_s=$(printf '%s\n' "$SUB_STRING" |\
sed -e 's:[][\/.^$*]:\\&:g' -e 'H;1h;$!d;g;s/\n/\\n/g')
printf '%s\n' "$STRING" |
sed -ne '
$!{N;s/^/\n/;D;}
/'"$esc_s"'/{
x;p;x
s///;s/^/\n/;D
}
' | wc -l
°準備段階として、メタ文字として機能するすべての文字をエスケープして、サブ文字列内のs ///ステートメントの左側に配置します。そうしないと、sedがクラッシュします。
°次に、文字列全体をパターンスペースにスラップします。
°次に、空の行を印刷し続けます。ホールドスペースは適切な候補であり、パターンスペースからサブストリングを削除します。
°リンス...泡...サブストリングが存在する限り繰り返します。
°次に、空の行がwcツールにパイプされ、行数=サブストリングが見つかった回数が取得されます。
これはシェルバージョンです:
e=$STRING N=0
while
e=$(expr " $e" : " \(.*\)$SUB_STRING")
case $e in "" ) break ;; esac
do
N=$(expr "$N" + 1)
done
echo "$N"
gawk '
END { print NR - 1 }
' RS='Bluetooth
Soft blocked: no
Hard blocked: no' input.txt
説明
RS
-入力レコード区切り文字、デフォルトでは改行。必要な文字列に設定すると、awk
はこの文字列をセパレータとして使用して、すべてのテキストをレコードに分割します。次に、END
セクションの1
によって減算されたレコード数を出力するだけです。
変数の使用:
#!/bin/bash
STRING='0: asus-wlan: Wireless LAN
Soft blocked: no
Hard blocked: no
1: asus-bluetooth: Bluetooth
Soft blocked: no
Hard blocked: no
2: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
113: hci0: Bluetooth
Soft blocked: no
Hard blocked: no'
SUB_STRING='Bluetooth
Soft blocked: no
Hard blocked: no'
gawk 'END { print NR - 1 }' RS="$SUB_STRING" <<< "$STRING"
Pythonのように使用できます この質問
python -c 'print "abcdabcva".count("ab")'
または、シェル変数を使用している場合:
python -c 'print("""'"$STRING"'""".count("""'"$SUB_STRING"'"""))'
あなたの場合:
python -c 'print """0: asus-wlan: Wireless LAN
Soft blocked: no
Hard blocked: no
1: asus-bluetooth: Bluetooth
Soft blocked: no
Hard blocked: no
2: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
113: hci0: Bluetooth
Soft blocked: no
Hard blocked: no""".count("""Bluetooth
Soft blocked: no
Hard blocked: no""")'
Gnugrepがあれば、次のように実行できます。
grep -zPio 'Bluetooth\s+Soft blocked: no\s+Hard blocked: no' ex.txt | grep -zc .