web-dev-qa-db-ja.com

文字列内の部分文字列の出現回数を数える

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を使用しています!

5
Eduardo Lucio

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

文字列処理関数を使用して、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"
3
Rakesh Sharma
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"
2
MiniMax

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""")'
2
Katu

Gnugrepがあれば、次のように実行できます。

grep -zPio 'Bluetooth\s+Soft blocked: no\s+Hard blocked: no' ex.txt | grep -zc .

1
JJoao