sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

string-split-lines.sh (2711B)


      1 #!/usr/bin/env bash
      2 
      3 source ../../src/benchmark.sh
      4 
      5 
      6 text='
      7 echo hello 2
      8 echo hello 3
      9 echo hello 4
     10 
     11 echo hello 6
     12 echo hello 7
     13 echo hello 8
     14 
     15 
     16 echo hello 11
     17 '
     18 
     19 function check-split-implementation {
     20   local fname=$1 ret
     21   "$fname" "$text"
     22   if ((${#ret[@]}!=12)); then
     23     echo "$fname: wrong result"
     24     declare -p ret
     25   fi >&2
     26 }
     27 
     28 #------------------------------------------------------------------------------
     29 # 実装1: 改行を置換 & eval arr=()
     30 
     31 function split1 {
     32   local text=$1 sep='' esc='\'
     33   if [[ $text == *$sep* ]]; then
     34     a=$esc b=$esc'A' text=${text//"$a"/"$b"}
     35     a=$sep b=$esc'B' text=${text//"$a"/"$b"}
     36 
     37     text=${text//$'\n'/"$sep"}
     38     GLOBIGNORE=\* IFS=$sep eval 'arr=($text$sep)'
     39 
     40     ret=()
     41     local value
     42     for value in "${arr[@]}"; do
     43       if [[ $value == *$esc* ]]; then
     44         a=$esc'B' b=$sep value=${value//"$a"/"$b"}
     45         a=$esc'A' b=$esc value=${value//"$a"/"$b"}
     46       fi
     47       ret[${#ret[@]}]=$value
     48     done
     49   else
     50     text=${text//$'\n'/"$sep"}
     51     GLOBIGNORE=\* IFS=$sep eval 'ret=($text$sep)'
     52   fi
     53 }
     54 
     55 check-split-implementation split1
     56 function split1.measure { split1 "$text"; }
     57 ble-measure split1.measure
     58 
     59 #------------------------------------------------------------------------------
     60 # 実装2: 正規表現で一つずつ切り出し
     61 
     62 function split2 {
     63   local text=$1 rex=$'[^\n]*'
     64   ret=()
     65   while :; do
     66     [[ $text =~ $rex ]]
     67     ret[${#ret[@]}]=$BASH_REMATCH
     68     text=${text:${#BASH_REMATCH}}
     69     [[ $text ]] || break
     70     text=${text:1}
     71   done
     72 
     73   # 以下のようにしてみたが微妙に遅くなった。
     74   # ${#BASH_REMATCH} の計算を変数に保存しても変わらない。
     75   # while :; do
     76   #   [[ $text =~ $rex ]]
     77   #   ret[${#ret[@]}]=$BASH_REMATCH
     78   #   ((${#BASH_REMATCH}<${#text})) || break
     79   #   text=${text:${#BASH_REMATCH}+1}
     80   # done
     81 }
     82 
     83 check-split-implementation split2
     84 function split2.measure { split2 "$text"; }
     85 ble-measure split2.measure
     86 
     87 #------------------------------------------------------------------------------
     88 # 実装3: mapfile を使う
     89 
     90 function split3 {
     91   mapfile -t ret <<< "$1"
     92 }
     93 check-split-implementation split3
     94 function split3.measure { split3 "$text"; }
     95 ble-measure split3.measure
     96 
     97 #------------------------------------------------------------------------------
     98 # 実装4: グロブパターンで一つずつ切り出し
     99 
    100 function split4 {
    101   local text=$1 value
    102   ret=()
    103   while :; do
    104     if [[ $text == *$'\n'* ]]; then
    105       value=${text%%$'\n'*}
    106       ret[${#ret[@]}]=$value
    107       text=${text#*$'\n'}
    108       #text=${text:${#value}+1}
    109     else
    110       ret[${#ret[@]}]=$text
    111       break
    112     fi
    113   done
    114 }
    115 check-split-implementation split4
    116 function split4.measure { split4 "$text"; }
    117 ble-measure split4.measure
    118