ShellScript
Zsh
fish
kotoha

Kotohaを使って実行前のコマンドラインにアニメの名言を入れるzshスクリプト

More than 1 year has passed since last update.

今なんでもと書かれていたのでShell Script Advent Calendar 2017 - Qiita5日目ですが普段の記事です。

使い方

isone.gif

ブラウザと異なり/は多用するので@/をトリガーに変更。
@/thogeでタグ検索。@/khogeでキーワード検索です。
現状トリガーそのまま流す方法を用意していないので使わなさそうなのを。
Ajaxのような非同期通信は無理なので、エンターの実行で初めて通信・選択し、コマンド編集中に戻ってきます。

使いみちは特に無いです。
wallコマンドで君の好きな名言を叫ぼう!
自鯖立ててスニペットをタグ付きで登録していくなんて使い方もできるかな。
zshの設定ファイルの共有で十分かな。

bash版も作れそうだけど力尽きたー!

コード

isone.zsh
autoload -Uz add-zsh-hook
add-zsh-hook preexec -isone

-isone () {
  local cmd="${1}"

  local root_url='https://kotoha-server.herokuapp.com'
  local tag_pattern='@/t(\S+)'
  local keyword_pattern='@/k(\S+)'
  local query
  local replace_pattern

  #setopt BASH_REMATCH; を使えば$matchは無し。動きが微妙に違うので注意
  if [[ $cmd =~ $tag_pattern ]]; then
    query='?tag='$match[1]
    replace_pattern=$tag_pattern
  elif [[ $cmd =~ $keyword_pattern ]]; then
    query='?text='$match[1]
    replace_pattern=$keyword_pattern
  else
    return 0
  fi

  # ""を外すならjqに-rオプション
  local words=`wget -q -O - "${root_url}/api/phrases.json${query}" | jq '.[].text'`
  if [ -z $words ]; then
    echo "${fg[red]}No Results : $match[1]${reset_color}"
  else
    local word=`echo $words | peco`
    if [ -z $word ]; then
      echo "${fg[red]}Abort peco${reset_color}"
    else
      cmd=`echo ${cmd} | sed -r "s#${replace_pattern}#${word}#"`
    fi
  fi

  print -z $cmd
  kill -INT 0
}

読み込み

ファイルを置いて読み込みか直接書く。
読み込みの場合はshebangが必要な場合があるかもしれません。

.zshrc
. (path)~/isone.zsh

dependencies

参考

見たものを無秩序に列挙

余話:そもそも

タイプ精度が悪すぎてコマンドの同じtypoをしたり、よく使うパスが頻出したりしていた…。
コマンドのtypo自体はこれで実行後に補正されるけれど、ちょっと遅い。
aliasはalias自体を忘れかねず、また求めていたのはコマンドの修正だったので、ちょっと違う。
abbr:fish流別名!入力後に展開される短縮コマンドを定義
がtypoコマンド修正の意図通りで、かなりGoodなんですが、文字列リテラルには非対応のあくまでコマンド限定。
両方書き換えたいという貪欲な欲求に、そう言えばコマンド実行前イベントがあってこれでコマンドの設定方法も知れたので、
あとは実行前に実行中断できればコマンド全体を好きに置換することができるなと調査しました。
結果ExeCopを見つけるものの、fishではkill -INT 0が効かず断念。
zshで実現するかと入れているとき、常用しないもので実用的なものを作っても仕方ないと思い、同時期にちょうどFx版作りたいなと思っていたKotohaを動かすことに。こっちの方が面白いので。
だから本来はfishでやりたかったので、ご存じの方は中断方法を教えていただけるとうれしいです…

Bash版(挫折)

月曜日に投稿しようとしたらshellscriptのアドベントカレンダーを見つけたので空いた火曜日に投稿することにしました。
空いた月曜でbash移植しようとおもったのですが…

そもそものExeCopが正常に動かない(denyがループに陥る)
という状況で困った…
プリントデバッグではkill -INT 0で中断しようとしているコマンドで再度trapが呼び出されるようで、(confirm no or denyの間)無限ループに。killの次の行に行かない。
手動で打つecho $$とtrap内でのecho $$のPIDの値が同じなので、まるごとkillからの復帰→コマンド継続とかになっている…?

ただ、ExeCopのアイデアもとになったスクリプトも同じ現象なので、異なる人が成功している時点でこちらの環境・バージョン依存濃厚です。

GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)

yumに更新があったので(4.2.46-29.el7_4)上げて
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

もループしました。うーん…。

それでも移植するだけしてみたら、killによる中断は出来るが、なぜかtrapが一度しか反応せず、以後キーワードを入れても無視される現象に変わる。

そもそもprint -zの代替が見つからないということで、ギブアップ。

正常に動かないコード
-isone () {
  local cmd="${1}"

  local root_url='https://kotoha-server.herokuapp.com'
  local tag_pattern='@/t(\S+)'
  local keyword_pattern='@/k(\S+)'
  local query
  local replace_pattern

  if [[ $cmd =~ $tag_pattern ]]; then
    query='?tag='${BASH_REMATCH[1]}
    replace_pattern=$tag_pattern
  elif [[ $cmd =~ $keyword_pattern ]]; then
    query='?text='${BASH_REMATCH[1]}
    replace_pattern=$keyword_pattern
  else
    return 0
  fi

  # ""を外すならjqに-rオプション
  local words="`wget -q -O - "${root_url}/api/phrases.json${query}" | jq '.[].text'`"
  if [ -z "$words" ]; then
    echo -e "\e[31mNo Results : $match[1]\e[m"
  else
    # 改行残して出力するため""追加
    local word=`echo "$words" | peco --layout top-down`
    if [ -z "$word" ]; then
      echo -e "\e[31mAbort peco\e[m"
    else
      cmd=`echo ${cmd} | sed -r "s#${replace_pattern}#${word}#"`
    fi
  fi

  #挿入方法不明
  COMP_LINE=$cmd
  READLINE_LINE="$cmd"
  kill -INT 0
}


# ここExeCopそのまま拝借
isone () {
    [ -n "$COMP_LINE" ] && return
    [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
    local cmd=''
    cmd=$(HISTTIMEFORMAT='' history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//");
    -isone "${cmd}"
}

# クォートしても最初に-があるとオプションとみなされもよう
trap 'isone' DEBUG


マネジメントとなぜかITエンジニア論がしばしば出てくるSHIROBAKO Advent Calendarは(なんと!)2017年も開催中!です!


  1. 不満は無いのですが、他を知らないので選んだ感がほしい。