sqlite 回転コマンドの作成

# まえがき
sqliteコマンドで回転コマンド作成した。right_stairが難しかった。

# 参考文献

https://stackoverflow.com/questions/21388820/how-to-get-the-last-index-of-a-substring-in-sqlite


# 環境

```bash
$sqlite3 --version
-- Loading resources from /home/sqlite/.sqliterc
3.30.0 2019-10-04 15:03:17 c20a35336432025445f9f7e289d0cc3e4003fb17f45a4ce74c6269c407c6e09f
$bash --version
GNU bash, バージョン 5.0.0(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```

# スクリプト

## 0.~/.sqliterc

```bash

$cat ~/.sqliterc
.mode column
.headers off
.width 0
```


## 1.left_stair.sh

先頭要素取得して末尾要素に追加していくパターン。

```bash
$cat ./left_stair.sh
#!/bin/bash

usage(){
  cat <<EOF
Usage:
\$$0 '\U1f4a'{0..9}
\$echo -e '\U1f4a'{0..9} | $0
EOF
}

chk_args(){
  declare -a argv=()
  local argv=($@)
  local argc="${#argv[@]}"
  [ "${argc}" -le 1 ] && usage && exit 1
}

circle(){
  echo -e "$@" | tr ' ' ',' | xargs -I@ bash -c 'sqlite3 testdb \
  "
  with
    sub(rsv_args,ele_cnt) as (select \"@\" as rsv_args,length(\"@\")-length(replace(\"@\",\",\",\"\"))+1 as ele_cnt)
    ,rec(rn,rsv_args,ele_cnt,ele,rest_args,done_args,rotate_args)as(
      select
        1
        ,rsv_args
        ,ele_cnt
        ,substr(rsv_args,1,instr(rsv_args,\",\")-1)
        ,substr(rsv_args,instr(rsv_args,\",\")+1)
        ,substr(rsv_args,1,instr(rsv_args,\",\")-1)
        ,rsv_args
      from sub
      union all
      select
        rn+1
        ,rsv_args
        ,ele_cnt
        ,case when substr(rest_args,1,instr(rest_args,\",\")-1)=\"\" then rest_args else substr(rest_args,1,instr(rest_args,\",\")-1) end
        ,case when substr(rest_args,1,instr(rest_args,\",\")-1)=\"\" then \"\" else substr(rest_args,instr(rest_args,\",\")+1) end
        ,done_args||\",\"||case when substr(rest_args,1,instr(rest_args,\",\")-1)=\"\" then rest_args else substr(rest_args,1,instr(rest_args,\",\")-1) end
        ,rest_args||\",\"||done_args
      from rec
      where rn<ele_cnt
    )select rotate_args from rec;
  "
' && rm testdb
}

main(){
  local init_args="$(cat -)";
  chk_args "${init_args}"
  while read ln;do circle "${ln}";done <<<"${init_args}"
}

[ -p /dev/stdin ] && cat - | main
[ -p /dev/stdin ] || echo -ne "$@" | main
```

先頭要素が次の対象行の末尾要素に追加されていく様子がわかる。

```bash
$./left_stair.sh '\U1f4a'{0..9}
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨
$echo -e '\U1f4a'{0..9} | ./left_stair.sh
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨
```

## 2.right_stair.sh

末尾要素取得して先頭要素に追加していくパターン。

```bash
$cat ./right_stair.sh
#!/bin/bash

usage(){
  cat <<EOF
Usage:
\$$0 '\U1f4a'{0..9}
\$echo -e '\U1f4a'{0..9} | $0
EOF
}

chk_args(){
  declare -a argv=()
  local argv=($@)
  local argc="${#argv[@]}"
  [ "${argc}" -le 1 ] && usage && exit 1
}

circle(){
  echo -e "$@" | tr ' ' ',' | xargs -I@ bash -c 'sqlite3 testdb \
    "
    with
      sub(rsv_args,ele_cnt) as (select \"@\" as rsv_args,length(\"@\")-length(replace(\"@\",\",\",\"\"))+1 as ele_cnt)
      ,rec(rn,rsv_args,ele_cnt,ele,rest_args,done_args,rotate_args)as(
        select
          1
          ,rsv_args
          ,ele_cnt
          ,replace(rsv_args,rtrim(rsv_args,replace(rsv_args,\",\",\"\")),\"\")
          ,rtrim(rtrim(rsv_args,replace(rsv_args,\",\",\"\")),\",\")
          ,replace(rsv_args,rtrim(rsv_args,replace(rsv_args,\",\",\"\")),\"\")
          ,replace(rsv_args,rtrim(rsv_args,replace(rsv_args,\",\",\"\")),\"\")||\",\"||rtrim(rtrim(rsv_args,replace(rsv_args,\",\",\"\")),\",\")
        from sub
        union all
        select
          rn+1
          ,rsv_args
          ,ele_cnt
          ,replace(rest_args,rtrim(rest_args,replace(rest_args,\",\",\"\")),\"\")
          ,rtrim(rtrim(rest_args,replace(rest_args,\",\",\"\")),\",\")
          ,replace(rest_args,rtrim(rest_args,replace(rest_args,\",\",\"\")),\"\")||\",\"||done_args
          ,replace(rest_args,rtrim(rest_args,replace(rest_args,\",\",\"\")),\"\")||\",\"||done_args||
            case when rtrim(rtrim(rest_args,replace(rest_args,\",\",\"\")),\",\")=\"\" then \"\" else \",\" end ||rtrim(rtrim(rest_args,replace(rest_args,\",\",\"\")),\",\")
        from rec
        where rn<ele_cnt
      )select rotate_args from rec;
    "
  ' && rm testdb
}

main(){
  local init_args="$(cat -)";
  chk_args "${init_args}"
  while read ln;do circle "${ln}";done <<<"${init_args}"
}

[ -p /dev/stdin ] && cat - | main
[ -p /dev/stdin ] || echo -ne "$@" | main
```

末尾要素が先頭要素に追加されていく様子がわかる。

```bash
$./right_stair.sh '\U1f4a'{0..9}
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩
$echo -e '\U1f4a'{0..9} | ./right_stair.sh
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩

```

## 3.square.sh

上記スクリプトをそれぞれ左と右、右と左に並べて、与える引数を正順と逆順の2つにしたパターン。
サブシェルの標準エラー出力が親シェルまで引き継がれてきてエラー出力してきたから、**2>/dev/null**でミュート。
プロセス置換だと、こういう動きになるんかな。

```bash
$cat ./square.sh 
#!/bin/bash

usage(){
  cat <<EOF
Usage:
\$$0 '\U1f4a'{0..9}
\$echo -e '\U1f4'{a..b}{{0..9},{A..F}} | $0
\$$0 '\U1f4a'{{0..9},{A..F}}
EOF
}

chk_args(){
  declare -a argv=()
  local argv=($@)
  local argc="${#argv[@]}"
  [ "${argc}" -le 1 ] && usage && exit 1
}

circle(){
  paste -d' ' <(echo "$@" | ./left_stair.sh 2>/dev/null) <(echo "$@" | rev | ./right_stair.sh 2>/dev/null)
  paste -d' ' <(echo "$@" | ./right_stair.sh 2>/dev/null) <(echo "$@" | rev | ./left_stair.sh 2>/dev/null)
}

main(){
  local init_args="$(cat -)";
  chk_args "${init_args}"
  while read ln;do circle "${ln}";done <<<"${init_args}"
}

[ -p /dev/stdin ] && cat - | main
[ -p /dev/stdin ] || echo -ne "$@" | main
```

なんかちょっといつもと違うけど、いいか。

```bash
$./square.sh '\U1f4a'{0..9}
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩 💠,💩,💨,💧,💦,💥,💤,💣,💢,💡
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠 💡,💠,💩,💨,💧,💦,💥,💤,💣,💢
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡 💢,💡,💠,💩,💨,💧,💦,💥,💤,💣
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢 💣,💢,💡,💠,💩,💨,💧,💦,💥,💤
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣 💤,💣,💢,💡,💠,💩,💨,💧,💦,💥
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤 💥,💤,💣,💢,💡,💠,💩,💨,💧,💦
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥 💦,💥,💤,💣,💢,💡,💠,💩,💨,💧
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦 💧,💦,💥,💤,💣,💢,💡,💠,💩,💨
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧 💨,💧,💦,💥,💤,💣,💢,💡,💠,💩
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨 💩,💨,💧,💦,💥,💤,💣,💢,💡,💠
💩,💠,💡,💢,💣,💤,💥,💦,💧,💨 💩,💨,💧,💦,💥,💤,💣,💢,💡,💠
💨,💩,💠,💡,💢,💣,💤,💥,💦,💧 💨,💧,💦,💥,💤,💣,💢,💡,💠,💩
💧,💨,💩,💠,💡,💢,💣,💤,💥,💦 💧,💦,💥,💤,💣,💢,💡,💠,💩,💨
💦,💧,💨,💩,💠,💡,💢,💣,💤,💥 💦,💥,💤,💣,💢,💡,💠,💩,💨,💧
💥,💦,💧,💨,💩,💠,💡,💢,💣,💤 💥,💤,💣,💢,💡,💠,💩,💨,💧,💦
💤,💥,💦,💧,💨,💩,💠,💡,💢,💣 💤,💣,💢,💡,💠,💩,💨,💧,💦,💥
💣,💤,💥,💦,💧,💨,💩,💠,💡,💢 💣,💢,💡,💠,💩,💨,💧,💦,💥,💤
💢,💣,💤,💥,💦,💧,💨,💩,💠,💡 💢,💡,💠,💩,💨,💧,💦,💥,💤,💣
💡,💢,💣,💤,💥,💦,💧,💨,💩,💠 💡,💠,💩,💨,💧,💦,💥,💤,💣,💢
💠,💡,💢,💣,💤,💥,💦,💧,💨,💩 💠,💩,💨,💧,💦,💥,💤,💣,💢,💡
```


# あとがき

sqliteは提供されているファンクションがその他のDBベンダよりもコンパクトだから、ちょっと大変だった。

以上、ありがとうございました。