パスワードを忘れた? アカウント作成
2020477 journal
UNIX

Yoh2の日記: シェルスクリプト (bash) で文字列分割 16

日記 by Yoh2

[2012-03-09 23:35 str3に関する条件が紛らわしかったので補足しました]

久し振りにシェルスクリプト (bash) を書いていたら、もっと簡単に実現できそうだけどやり方を思い付かないというものが出てきたのでメモ。

やりたいこと:
シェル変数vに"str1:str2:str3"のようにコロン区切りされた3つの文字列が入っている時に、シェル変数aにstr1、シェル変数bにstr2、シェル変数cにstr3を格納したい。
その他条件は以下の通り。

  • str1は空文字列の可能性がある。その時はaに空文字列を設定する。
  • str3にコロンが含まれている可能性がある。その時はcにコロンも含めた文字列を設定する。(例: v="aaa:bbb:ccc:ddd"の時は、cは"ccc:ddd"となって欲しい(単に"ccc"ではなく))
  • できる限り、シェルの機能と一般的なコマンド (目安としてbusyboxに入っているもの) で実現すること。

とりあえず以下のコードで実現できた。同じ文字列に対してcutを3回使っているのが何となく気に入らない。echoと組み合わせるのも何か大仰な感じがするし。

a="$(echo -n "$v" | cut -d: -f1)"
b="$(echo -n "$v" | cut -d: -f2)"
c="$(echo -n "$v" | cut -d: -f3-)"

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • a="${v%%:*}"
    b="${v%:*}"
    b="${b#*:}"
    c="$}v##*:}"
    みたいなの、kshと同様にbashでも使えませんでしたっけ? 変数に修められた文字列にマッチする最短/最長の先頭/末尾を切り取ったものが得られる文字列っぽいの。
    真ん中を取り出すときが美しくないのはわたしがヘボいからです。

    • アドバイスありがとうございます。
      本文が誤解を招く表現になっていた (修正しました) ので、後出し気味の話になってしまいますが、v="aaa:bbb:ccc:ddd"のように、str3にコロンが入るとうまくいかないんですよね、これ。

      --
      巧妙に潜伏したバグは心霊現象と区別が付かない。
      親コメント
    • by Anonymous Coward

      cr='CAR:CADR:CADDR:CADDDR:CDDDDR'
      d=''
      while [ ${#d} -le 1 ] ; do
          cmds='r="${c'$d'r#*:}";ca'$d'r="${c'$d'r%%:*}"'
          d="d$d"
          eval "c$d$cmds"
      done
      echo "$car , $cadr , $cddr"

  • http://d.hatena.ne.jp/dharry/20090211/1234290856 [hatena.ne.jp]

    #! /bin/bash
     
    AFO='hoge:hage:hure'
     
    a=${AFO%%:*}
    bsub=${AFO#*:}
    b=${bsub%:*}
    c=${AFO##*:}
     
    echo $a
    echo $bsub
    echo $b
    echo $c

    ただ、移植性が悪くなることと、あまり柔軟性がない点は覚悟が必要。それなら

    #! /bin/bash
     
    AFO='hoge:hage:hure'
     
    d=$(echo ${AFO} | sed -r 's/([^:]*):([^:]*):([^:]*)/\1/')
    e=$(echo ${AFO} | sed -r 's/([^:]*):([^:]*):([^:]*)/\2/')
    f=$(echo ${AFO} | sed -r 's/([^:]*):([^:]*):([^:]*)/\3/')
     
    echo $d
    echo $e
    echo $f

    と、全てを sed の拡張正規表現様にお任せする方が柔軟度が高いというものです (-人-)
    # 南無正規表現如来

    --
    fjの教祖様
    • アドバイスありがとうございます。

      最初の例については、shibuyaさんのバージョンと同様に、str3にコロンが入ってくる形だとうまくいかなくなってしまいますね。
      後者のsedを使うバージョンについては、s/([^:]*):([^:]*):(.*)/……/とすれば対応可能そうです。
      本文に書いた、cutを使う方法と同様に、同じようなコマンドが連続するのが気になりますが、evalと組み合わせれば1回で済みそうですね。

      eval $(echo "$v" | sed -r 's/([^:]*):([^:]*):(.*)/a='\''\1'\''; b='\''\2'\'';  c='\''\3'\'';/')

      ……て、わかりづらいな。3日後にこれを解読できる自信がありません(汗

      --
      巧妙に潜伏したバグは心霊現象と区別が付かない。
      親コメント
      • eval $(echo "$v" | sed -r 's/([^:]*):([^:]*):(.*)/a='\''\1'\''; b='\''\2'\'';  c='\''\3'\'';/')

        あ。vにシングルクォートを含めたら破綻した(- -;;

        --
        巧妙に潜伏したバグは心霊現象と区別が付かない。
        親コメント
      • あぁっ、最後の条件を忘れてた。

        同じようなコマンドが連続するのが気になりますが、

        そこを気にするぐらいなら、全体の制御の方を bash ではなく perl で書くことを選びます。
        # 拡張正規表現の関係で perl を選ぶ。

        というか、ちょうどその辺りが shell で済ませるか、もっと制御機能の付いたスクリプト言語に移行するかの境目ですね。

        --
        fjの教祖様
        親コメント
        • by Anonymous Coward

          split()
          {
          IFS=':' read a b c
          echo "$a"
          echo "$b"
          echo "$c"
          }

          declare -a argv
          argv=($(echo '123:456:789:ABC' | split))
          echo "${argv[0]}"
          echo "${argv[1]}"
          echo "${argv[2]}"

          • なるほど。readはほとんど使ったことがないので参考になります。

            おや、直接 echo ... | read ... だとダメなのか。man確認しとこう。

            --
            巧妙に潜伏したバグは心霊現象と区別が付かない。
            親コメント
            • by Anonymous Coward

              パイプがあると別の世界(サブシェル)なので、変数はサブシェルのなかでのみ有効です。
              どんなことがやりたいかわからないので、親シェルに値を返すためにあんなことをしていますが、
              一番単純なパタンだと、これで十分です。
              echo 1:2:3 | (IFS=':' read a b c; ここに処理を書く)

              • by Anonymous Coward

                ヒアドキュメントを使えば、read で読ませることもできますよ。

                IFS=:  read a b c <<EOF
                123:456:789
                EOF

  • by Anonymous Coward on 2012年03月09日 23時14分 (#2114764)

    #!/bin/bash
    TEXT='AAA;hoge;2345'
    IFS=';'
    set -- $TEXT
    echo $1
    echo $2
    echo $3

typodupeerror

※ただしPHPを除く -- あるAdmin

読み込み中...