CUIでお洒落な開発環境を整えよう
はじめに
みなさん年の瀬いかがお過ごしでしょうか.こたつスウィングバイが止まらないさわだです.
工大アドベントカレンダー(一日目)の空枠を拝借して,自分も最近の事を書こうと思います.
今回の記事はこれです.
いっつも隣でやばいtmuxひらいてるからw
— けんつ (@lrf141) 2018年12月19日
きっかけ
我が愛機であるThinkpad X220にArch LinuxをホストOSとしてインストールしましたが,chrome君とslack君に加え,Intellijの子供たちを使うとメモリが逼迫するのでCUIで開発環境を整えたいなと思ったのがきっかけです.
整備する
以前はneovim周辺の設定を弄って満足出来ましたが,あらゆる事をCUIで完結させたい欲が高まるにつれ,複数のターミナルを起動するのも億劫になってきました. 更にCUIでも毎日触るのが楽しくなるような見た目が欲しくなりました. となると...自分にとって必要な環境整備は以下の項目が挙げられます.
CUIツールの導入
ターミナルマルチプレクサの導入(且つ,ばぁぁーーんといい感じに起動したい)
諸々の見た目の整備
では順を追って解説に入ります.
CUIツール
必要なものはすべて偉大なるArch Wikiが教えてくれました.アプリケーション一覧
にあるコンソール
に様々な紹介があります*1.
システム監視として見た目がいい感じかつGo製のgotop,ファイルマネージャとして操作感がvimに似たranger,作業BGMを聴くためにプレイリストが管理できるmps-youtube,IRCクライアントとしてweechatを導入しました.
Archではyay -S <pkg>
でこれら全てが楽にインストール出来ます.他OSもドキュメントに従えば楽にツールを導入できます.
gotop
vimに似た操作感でプロセス管理も可能です.詳しくはリポジトリのREADMEを読めばいいと思います.nodejs製のgtopはモニタリングツールとして有名ですが,CUIで使うにはちょっと軽量とは言い難い部分があります.私の環境ではgotopによりメモリ使用量はgtop 85MB
から gotop 7.3MB
へ節約できました.gotop -c solarized
でカラースキーム指定起動するとこんな画面です.tmux上ではminimal表示を利用しています.
ranger
vimに似た操作感でファイル操作ができます.設定ファイルは.config/ranger
配下に作られるので,dotfilesによる設定管理も容易です.自分は見た目のカスタマイズにranger_deviconsを使っています.カラースキームはsnowを採用.
mps-youtube
作業用にyoutubeにあるnightcoreを聴きたいけれど,ブラウザは使いたくない自分がたどり着いたのはこのツールでした.Python製です.操作キーは上記2つのツールに比べると若干特殊なため覚える必要があります.起動はmpsyt
.操作方法は以下の記事を見ればよいかと.自分はキーワード検索/hogefuga
して全曲再生all
,またはプレイリスト再生pl <playlist ID>
を使うことが多いです.
weechat
IRCチャット用に軽量なweechatは様々なプラグインで拡張が出来ます.ただし,プラグインは言語依存があるので注意が必要です.入りたいIRCサーバを追加したり,バッファにチャンネルリストを表示したり,色々設定すればGUIに負けないチャットツールとして重宝できるでしょう.今は開発者コミュニティで情報収集やPKhack界隈のチャットとして使っていますが,できればtwitterやslackもweechatで表示したいと格闘中...
ターミナルマルチプレクサ
日本語の記事も多いtmuxを導入しました.sshを多用する場面でも何かと重宝するので以前から入っていましたが,本格的に設定したのは今回が初めてです. tmuxをいい感じに起動するためにセッションマネージャとしてtmuxpを導入します.
これは,競合であるtmuxinatorがRuby依存だったのに対して,tmuxpがpython依存だったからです(すでにpyenv+pipenvで環境構築していたため).この辺は好きなセッションマネージャを導入すればいいと思います.設定にはyamlを使います.
tmux_options: -2 session_name: work root: ~/CTF/ windows: - window_name: main layout: 86f2,151x37,0,0{84x37,0,0[84x22,0,0,2,84x14,0,23,6],66x37,85,0[66x10,85,0,3,66x26,85,11,4]} panes: - screenfetch - gotop -c solarized -m - ranger - mpsyt pl PLBFICLTzW2LckbdM1Ri6BBfpeSPwRYzXe - window_name: chat panes: - weechat - window_name: work focus: true layout: cab4,151x37,0,0{92x37,0,0,6,58x37,93,0,7} panes: - pane - pane
layout
の項目はtmux起動中にtmux list-windows
で調べることが出来ます.上記のような設定を書いて.tmux
配下などに保存しておきtmuxp load path/to/config.yaml
で起動するとウィンドウが複数立ち上がり,分割されたペイン上でそれぞれのCUIツールを走らせることが出来ます.大変便利.
諸々の見た目
Nerd Fonts
ターミナル上にもアイコン系ロゴ系フォントを導入します.理由はascii artのような見た目の楽しさと,neovimで既にairline用としてpowerline系のフォントを使っていたからです. 有名所をまとめた素晴らしいロゴフォントとしてNerd Fontsがあります.
このフォントを普段使うターミナル用フォントにfontforgeで合成し設定することで,ターミナルの表示はガラリと変化するはず.手っ取り早く導入したい方は日本語プログラミング環境用等幅フォントのCica fontをおすすめします.ライセンス関係で一部表示できないため,どうしてもアイコン類を表示したい方はfontforgeを使って上書きすれば良いかと.日本語表示には影響がないので...
ターミナルでの表示確認用にシェルスクリプトを書きました.もともとNerd Fontsに同封されていた使いづらいテストコードをコマンドツールとして改良したものになります.
#!/usr/bin/env bash # Author: takuzoo3868 # Last Modified: 07 Dec 2018. # Check Nerd fonts drawing on terminal. # Nerd Fonts Version: 2.0.0 # Script Version: 1.0.0 PROGNAME=$(basename $0) VERSION="1.0.0" usage() { echo "usage: ${PROGNAME} [--font_option]" echo "" echo "Check Nerd fonts v.2.0.0 drawing on terminal." echo "" echo "optional arguments:" echo " -h, --help show this help message and exit" echo " --dev Font Devicon" echo " --fa Font Awesome" echo " --fae Font Awesome Extension" echo " --iec IEC Power Symbols" echo " --linux, --fontlogos Font Linux and other open source Glyphs" echo " --material, --mdi Material Design Icon" echo " --oct Octicons" echo " --pl Powerline" echo " --ple Powerline Extra" echo " --pom Pomicon" echo " --seti Seti-UI + Custom" echo " --weather Weather Icons" exit 1 } # Given an array of decimal numbers print all unicode codepoint. function print-decimal-unicode-range() { local originalSequence=("$@") local counter=0 # Use alternating colors to see which symbols extend out of the bounding # box. local bgColorBorder='\033[48;5;8m' local bgColorCode='\033[48;5;246m' local alternateBgColorCode='\033[48;5;240m' local bgColorChar='\033[48;5;66m' local alternateBgColorChar='\033[48;5;60m' local underline='\033[4m' local currentColorCode="${bgColorCode}" local currentColorChar="${bgColorChar}" local reset_color='\033[0m' local allChars="" local allCodes="" local wrapAt=5 local topLine="${bgColorBorder}╔══════╦══════╦══════╦══════╦══════╗${reset_color}" local bottomLine="${bgColorBorder}╚══════╩══════╩══════╩══════╩══════╝${reset_color}" local line="${bgColorBorder}╠══════╬══════╬══════╬══════╬══════╣${reset_color}" local bar="${bgColorBorder}║${reset_color}" local originalSequenceLength=${#originalSequence[@]} local leftoverSpaces=$((wrapAt - (originalSequenceLength % wrapAt))) # add fillers to array to maintain table: if [[ "$leftoverSpaces" < "$wrapAt" ]]; then # shellcheck disable=SC2034 # needs rework without 'i' var? for i in $(seq 1 $leftoverSpaces); do originalSequence+=(0) done fi local sequenceLength=${#originalSequence[@]} printf "%b\\n" "$topLine" for decimalCode in "${originalSequence[@]}"; do local hexCode hexCode=$(printf '%x' "${decimalCode}") local code="${hexCode}" local char="\\u${hexCode}" # fill in placeholder cells properly formatted: if [ "${char}" = "\\u0" ]; then char=" " code=" " fi allCodes+="${currentColorCode} ${underline}${code}${reset_color}${currentColorCode} ${reset_color}$bar" allChars+="${currentColorChar} ${char} ${reset_color}$bar" counter=$((counter + 1)) count=$(( (count + 1) % wrapAt)) if [[ $count -eq 0 ]]; then if [[ "${currentColorCode}" = "${alternateBgColorCode}" ]]; then currentColorCode="${bgColorCode}" currentColorChar="${bgColorChar}" else currentColorCode="${alternateBgColorCode}" currentColorChar="${alternateBgColorChar}" fi printf "%b%b%b" "$bar" "$allCodes" "$reset_color" printf "\\n" printf "%b%b%b" "$bar" "$allChars" "$reset_color" printf "\\n" if [ "$counter" != "$sequenceLength" ]; then printf "%b\\n" "$line" fi allCodes="" allChars="" fi done printf "%b\\n" "$bottomLine" } function print-unicode-ranges() { echo '' local arr=($@) local len=$# local combinedRanges=() for ((j=0; j<len; j+=2)); do local start="${arr[$j]}" local end="${arr[(($j+1))]}" local startDecimal=$((16#$start)) local endDecimal=$((16#$end)) combinedRanges+=($(seq "${startDecimal}" "${endDecimal}")) done print-decimal-unicode-range "${combinedRanges[@]}" } for OPT in "$@" do case $OPT in '-h' | '--help' ) usage ;; '--dev' ) echo "Nerd Fonts - Devicons" print-unicode-ranges e700 e7c5 echo; echo ;; '--fa' ) echo "Nerd Fonts - Font awesome" print-unicode-ranges f000 f2e0 echo; echo ;; '--fae' ) echo "Nerd Fonts - Font awesome extension" print-unicode-ranges e200 e2a9 echo; echo ;; '--ice' ) echo "Nerd Fonts - Font Power Symbols" print-unicode-ranges 23fb 23fe 2b58 2b58 echo; echo ;; '--linux' | '--fontlogos') echo "Nerd Fonts - Font Linux" print-unicode-ranges f300 f31c echo; echo ;; '--material' | '--mdi') echo "Nerd Fonts - Material Design Icons" print-unicode-ranges f500 fd46 echo; echo ;; '--oct' ) echo "Nerd Fonts - Octicons" print-unicode-ranges 2665 2665 26A1 26A1 f400 f4a8 f67c f67c echo; echo ;; '--pl' ) echo "Nerd Fonts - Powerline" print-unicode-ranges e0a0 e0a2 e0b0 e0b3 echo; echo ;; '--ple' ) echo "Nerd Fonts - Powerline Extra" print-unicode-ranges e0a3 e0a3 e0b4 e0c8 e0cc e0d2 e0d4 e0d4 echo; echo ;; '--pom' ) echo "Nerd Fonts - Pomicons" print-unicode-ranges e000 e00a echo; echo ;; '--seti' ) echo "Nerd Fonts - Symbols original" print-unicode-ranges e5fa e62e echo; echo ;; '--weather' ) echo "Nerd Fonts - Weather Icons" print-unicode-ranges e300 e3eb echo; echo ;; -*) echo "$PROGNAME: illegal option -- '$(echo $1 | sed 's/^-*//')'" 1>&2 exit 1 ;; *) usage ;; esac done
Bash prompt
Nerd Fontsの導入で改善したい見た目その①はシェルのプロンプトでした.zshをお使いの方はPowerlevel9k
を使えばイケイケなプロンプトになります.
自分はbashのままかfishが多いので,プロンプトをPowerlevel9k
っぽく変更したいと思います.試行錯誤の末,完成したプロンプトがこちら.
といった表示を行っています.現時刻やipアドレスといった別ツールでも参照しやすい情報は後述するtmuxのステータスラインに表示することにしました.
Nerd Fontsを中てた上でbashrc
のPS1
を下記のように上書きすれば同様の表示となるはずです.
#!/usr/bin/env bash if [[ $COLORTERM = gnome-* && $TERM = xterm ]] && infocmp gnome-256color >/dev/null 2>&1; then export TERM='gnome-256color'; elif infocmp xterm-256color >/dev/null 2>&1; then export TERM='xterm-256color'; fi; if tput setaf 1 &> /dev/null; then tput sgr0; # reset colors bold=$(tput bold); reset=$(tput sgr0); black=$(tput setaf 234); blue=$(tput setaf 27); cyan=$(tput setaf 39); green=$(tput setaf 76); orange=$(tput setaf 166); purple=$(tput setaf 125); red=$(tput setaf 124); violet=$(tput setaf 61); white=$(tput setaf 15); yellow=$(tput setaf 154); else bold=''; reset="\e[00m"; black="\e[1;30m"; blue="\e[1;34m"; cyan="\e[1;36m"; green="\e[1;32m"; orange="\e[1;33m"; purple="\e[1;35m"; red="\e[1;31m"; violet="\e[1;35m"; white="\e[1;37m"; yellow="\e[1;33m"; fi; export PROMPT_DIRTRIM=2 # icons set ICON_HOME="" ICON_DIR="" ICON_ETC="" ICON_USER="" ICON_HOST="" ICON_OK="" ICON_FAIL="" ICON_LOCK="" ICON_NOT_FOUND="" ICON_STOP="" ICON_OCTOCAT="" ICON_GIT_BITBUCKET="" ICON_GIT_GITLAB="" ICON_GIT_BRANCH="" ICON_GIT_COMMIT="" ICON_GIT_REMOTE_BRANCH="" ICON_GIT_UNTRACKED="" ICON_GIT_UNSTAGED="" ICON_GIT_STAGED="" ICON_GIT_STASH="" ICON_GIT_INCOMING_CHANGES="" ICON_GIT_OUTGOING_CHANGES="" ICON_GIT_TAG="" # git status veiw prompt_git() { local s=''; local branchName=''; local gitHash=''; GIT_DIR="$(git rev-parse --git-dir 2>/dev/null)" # Check if the current directory is in a Git repository. if [ $(git rev-parse --is-inside-work-tree &>/dev/null; echo "${?}") == '0' ]; then # check if the current directory is in .git before running git checks if [ "$(git rev-parse --is-inside-git-dir 2> /dev/null)" == 'false' ]; then # Ensure the index is up to date. git update-index --really-refresh -q &>/dev/null; # Check for uncommitted changes in the index. if ! $(git diff --quiet --ignore-submodules --cached); then s+=${ICON_GIT_STAGED}; fi; # Check for unstaged changes. if ! $(git diff-files --quiet --ignore-submodules --); then s+=${ICON_GIT_UNSTAGED}; fi; # Check for untracked files. if [ -n "$(git ls-files --others --exclude-standard)" ]; then s+=${ICON_GIT_UNTRACKED}; fi; # Check for stashed files. if $(git rev-parse --verify refs/stash &>/dev/null); then s+=${ICON_GIT_STASH}; fi; fi; # Get the short symbolic ref. # If HEAD isn’t a symbolic ref, get the short SHA for the latest commit # Otherwise, just give up. branchName=" ${ICON_GIT_BRANCH} $(git symbolic-ref --quiet --short HEAD 2> /dev/null || \ git rev-parse --short HEAD 2> /dev/null || \ echo '(unknown)')"; [ -n "${s}" ] && s=" ${s}"; # Get commit hash gitHash=" ${ICON_GIT_COMMIT} $(git rev-parse --short HEAD)"; echo -e "${ICON_OCTOCAT}${1}${branchName}${gitHash}${2}${s}"; else return; fi; } prompt_dir_icon(){ case $PWD in $HOME) echo ${ICON_HOME} ;; "/etc") echo ${ICON_ETC} ;; *) echo ${ICON_DIR} ;; esac } prompt_user(){ if [[ "${USER}" == "root" ]]; then user_state="${orange}"; else user_state="${blue}"; fi; # the hostname when connected via SSH. if [[ "${SSH_TTY}" ]]; then hostStyle="${bold}${red}"; else hostStyle="${yellow}"; fi; echo -e "${user_state}${ICON_USER}" } prompt_host(){ echo -e "${cyan}${ICON_HOST}" } prompt_result() { code=$? if [ ${code} == 0 ]; then echo -e "${ICON_OK}"; elif [ ${code} == 126 ]; then echo -e "${ICON_LOCK}"; # Command invoked cannot execute elif [ ${code} == 127 ]; then echo -e "${ICON_NOT_FOUND}"; # Command not found elif [ ${code} == 130 ]; then echo -e "${ICON_STOP}"; # Script terminated by Control-C else echo -e "${ICON_FAIL} ${bold}${code}${reset}"; fi; } # Set the terminal title and prompt. PS1=" "; PS1+="\$(prompt_user) \[${bold}\]\u "; PS1+="\[${reset}\]"; PS1+="\$(prompt_host) \[${bold}\]\h "; PS1+="\[${reset}\]"; PS1+="\[${green}\]\$(prompt_dir_icon) \[${bold}\]\w "; PS1+="\[${reset}\]"; PS1+="\[${yellow}\]\$(prompt_git) "; PS1+="\[${reset}\]"; PS1+="\[${yellow}\]\$(prompt_result)"; PS1+="\[${reset}\]"; PS1+="\n"; PS1+="\$ "; export PS1;
Tmux status line
いい感じの見た目とすべく,昔は先人の情報をもとにtmux-powerlineを使っていました. しかし,dotfilesで管理しにくい点,及びpyenv環境ではpowerline設定がややこしい点から廃止し,ステータスラインをフルスクラッチすることにしました. 自分でも割とお気に入りの見た目を作ることが出来たと思っています.
メンテナンスしやすいようtmux.conf
とtmux_local.conf
へ分離したり,色々やってますが長くなるので割愛します.dotfilesにあるこいつを見てくれ!独自にステータスへスクリプトを組み込むことも可能です.自分の場合は例として,local IP / global IP / 天気情報をスクリプトで追加しています.
#!/usr/bin/env bash # # Author: takuzoo3868 # Last Modified: 25 Nov 2018. # Check OS ostype() { echo $OSTYPE | tr '[A-Z]' '[a-z]'; } export SHELL_PLATFORM='unknown' case "$(ostype)" in *'linux'* ) SHELL_PLATFORM='linux' ;; *'darwin'* ) SHELL_PLATFORM='osx' ;; *'bsd'* ) SHELL_PLATFORM='bsd' ;; esac shell_is_linux() { [[ $SHELL_PLATFORM == 'linux' || $SHELL_PLATFORM == 'bsd' ]]; } shell_is_osx() { [[ $SHELL_PLATFORM == 'osx' ]]; } shell_is_bsd() { [[ $SHELL_PLATFORM == 'bsd' || $SHELL_PLATFORM == 'osx' ]]; } export -f shell_is_linux export -f shell_is_osx export -f shell_is_bsd __run_lan() { if shell_is_bsd || shell_is_osx ; then all_nics=$(ifconfig 2>/dev/null | awk -F':' '/^[a-z]/ && !/^lo/ { print $1 }') for nic in ${all_nics[@]}; do ipv4s_on_nic=$(ifconfig ${nic} 2>/dev/null | awk '$1 == "inet" { print $2 }') for lan_ip in ${ipv4s_on_nic[@]}; do [[ -n "${lan_ip}" ]] && break done [[ -n "${lan_ip}" ]] && break done else # Get the names of all attached NICs. all_nics="$(ip addr show | cut -d ' ' -f2 | tr -d :)" all_nics=(${all_nics[@]//lo/}) # Remove lo interface. for nic in "${all_nics[@]}"; do # Parse IP address for the NIC. lan_ip="$(ip addr show ${nic} | grep '\<inet\>' | tr -s ' ' | cut -d ' ' -f3)" # Trim the CIDR suffix. lan_ip="${lan_ip%/*}" # Only display the last entry lan_ip="$(echo "$lan_ip" | tail -1)" [ -n "$lan_ip" ] && break done fi echo "ⓛ ${lan_ip-N/a}" return 0 } __run_lan
#!/usr/bin/env bash # # Author: takuzoo3868 # Last Modified: 25 Nov 2018. # Check OS ostype() { echo $OSTYPE | tr '[A-Z]' '[a-z]'; } export SHELL_PLATFORM='unknown' case "$(ostype)" in *'linux'* ) SHELL_PLATFORM='linux' ;; *'darwin'* ) SHELL_PLATFORM='osx' ;; *'bsd'* ) SHELL_PLATFORM='bsd' ;; esac shell_is_linux() { [[ $SHELL_PLATFORM == 'linux' || $SHELL_PLATFORM == 'bsd' ]]; } shell_is_osx() { [[ $SHELL_PLATFORM == 'osx' ]]; } shell_is_bsd() { [[ $SHELL_PLATFORM == 'bsd' || $SHELL_PLATFORM == 'osx' ]]; } export -f shell_is_linux export -f shell_is_osx export -f shell_is_bsd # Path tmp file export DIR_TEMPORARY="/tmp/tmux-weather_${USER}" if [ ! -d "$DIR_TEMPORARY" ]; then mkdir -p "$DIR_TEMPORARY" fi __run_wan() { local tmp_file="${DIR_TEMPORARY}/wan_ip.txt" local wan_ip if [ -f "$tmp_file" ]; then if shell_is_osx || shell_is_bsd; then stat >/dev/null 2>&1 && is_gnu_stat=false || is_gnu_stat=true if [ "$is_gnu_stat" == "true" ];then last_update=$(stat -c "%Y" ${tmp_file}) else last_update=$(stat -f "%m" ${tmp_file}) fi elif shell_is_linux || [ -z $is_gnu_stat]; then last_update=$(stat -c "%Y" ${tmp_file}) fi time_now=$(date +%s) update_period=900 up_to_date=$(echo "(${time_now}-${last_update}) < ${update_period}" | bc) if [ "$up_to_date" -eq 1 ]; then wan_ip=$(cat ${tmp_file}) fi fi if [ -z "$wan_ip" ]; then wan_ip=$(curl --max-time 2 -s http://whatismyip.akamai.com/) if [ "$?" -eq "0" ]; then echo "${wan_ip}" > $tmp_file elif [ -f "${tmp_file}" ]; then wan_ip=$(cat "$tmp_file") fi fi if [ -n "$wan_ip" ]; then echo "ⓦ ${wan_ip}" fi return 0 } __run_wan
#!/usr/bin/env bash # # Author: takuzoo3868 # Last Modified: 25 Nov 2018. # API: http://developer.yahoo.com/weather # # osx need coreutils # Check OS ostype() { echo $OSTYPE | tr '[A-Z]' '[a-z]'; } export SHELL_PLATFORM='unknown' case "$(ostype)" in *'linux'* ) SHELL_PLATFORM='linux' ;; *'darwin'* ) SHELL_PLATFORM='osx' ;; *'bsd'* ) SHELL_PLATFORM='bsd' ;; esac shell_is_linux() { [[ $SHELL_PLATFORM == 'linux' || $SHELL_PLATFORM == 'bsd' ]]; } shell_is_osx() { [[ $SHELL_PLATFORM == 'osx' ]]; } shell_is_bsd() { [[ $SHELL_PLATFORM == 'bsd' ]]; } export -f shell_is_linux export -f shell_is_osx export -f shell_is_bsd # Path tmp file export DIR_TEMPORARY="/tmp/tmux-weather_${USER}" if [ ! -d "$DIR_TEMPORARY" ]; then mkdir -p "$DIR_TEMPORARY" fi # DEFAULT WEATHER_DATA_PROVIDER_DEFAULT="yahoo" WEATHER_UNIT_DEFAULT="c" WEATHER_UPDATE_PERIOD_DEFAULT="600" # input your woeid https://lab.syncer.jp/Tool/WOEID-Lookup/ WEATHER_LOCATION_DEFAULT="1118108" export WEATHER_DATA_PROVIDER="${WEATHER_DATA_PROVIDER_DEFAULT}" # What unit to use. Can be any of {c,f,k}. export WEATHER_UNIT="${WEATHER_UNIT_DEFAULT}" # How often to update the weather in seconds. export WEATHER_UPDATE_PERIOD="${WEATHER_UPDATE_PERIOD_DEFAULT}" # Name of GNU grep binary if in PATH, or path to it. export WEATHER_GREP="${WEATHER_GREP_DEFAULT}" # Your location. Find a code that works for you: export WEATHER_LOCATION="${WEATHER_LOCATION_DEFAULT}" # Setting grep command if shell_is_bsd && [ -f /user/local/bin/grep ]; then WEATHER_GREP_DEFAULT="/usr/local/bin/grep" else WEATHER_GREP_DEFAULT="grep" fi __default_settings() { if [ -z "$WEATHER_DATA_PROVIDER" ]; then export WEATHER_DATA_PROVIDER="${WEATHER_DATA_PROVIDER_DEFAULT}" fi if [ -z "$WEATHER_UNIT" ]; then export WEATHER_UNIT="${WEATHER_UNIT_DEFAULT}" fi if [ -z "$WEATHER_UPDATE_PERIOD" ]; then export WEATHER_UPDATE_PERIOD="${WEATHER_UPDATE_PERIOD_DEFAULT}" fi if [ -z "$WEATHER_GREP" ]; then export WEATHER_GREP="${WEATHER_GREP_DEFAULT}" fi if [ -z "$WEATHER_LOCATION" ]; then echo "No weather location specified."; exit 8 fi } # Run status line in tmux __run_weather() { __default_settings local tmp_file="${DIR_TEMPORARY}/weather_yahoo.txt" local weather case "$WEATHER_DATA_PROVIDER" in "yahoo") weather=$(__yahoo_weather) ;; *) echo "Unknown weather provider [${$WEATHER_DATA_PROVIDER}]"; return 1 esac if [ -n "$weather" ]; then echo "$weather" fi } # Get the weather from Yahoo! __yahoo_weather() { degree="" if [ -f "$tmp_file" ]; then if shell_is_bsd; then last_update=$(stat -f "%m" ${tmp_file}) elif shell_is_linux || shell_is_osx; then last_update=$(stat -c "%Y" ${tmp_file}) fi time_now=$(date +%s) up_to_date=$(echo "(${time_now}-${last_update}) < ${WEATHER_UPDATE_PERIOD}" | bc) if [ "$up_to_date" -eq 1 ]; then __read_tmp_file fi fi if [ -z "$degree" ]; then weather_data=$(curl --max-time 4 -s "https://query.yahooapis.com/v1/public/yql?format=xml&q=SELECT%20*%20FROM%20weather.forecast%20WHERE%20u=%27${WEATHER_UNIT}%27%20AND%20woeid%20=%20%27${WEATHER_LOCATION}%27") if [ "$?" -eq "0" ]; then error=$(echo "$weather_data" | grep "problem_cause\|DOCTYPE"); if [ -n "$error" ]; then echo "error" exit 1 fi # Assume latest grep is in PATH gnugrep="${WEATHER_GREP}" # <yweather:units temperature="F" distance="mi" pressure="in" speed="mph"/> unit=$(echo "$weather_data" | "$gnugrep" -Zo "<yweather:units [^<>]*/>" | sed 's/.*temperature="\([^"]*\)".*/\1/') condition=$(echo "$weather_data" | "$gnugrep" -Zo "<yweather:condition [^<>]*/>") # <yweather:condition text="Clear" code="31" temp="66" date="Mon, 01 Oct 2012 8:00 pm CST" /> degree=$(echo "$condition" | sed 's/.*temp="\([^"]*\)".*/\1/') condition=$(echo "$condition" | sed 's/.*text="\([^"]*\)".*/\1/') # Pull the times for sunrise and sunset so we know when to change the day/night indicator # <yweather:astronomy sunrise="6:56 am" sunset="6:21 pm"/> if shell_is_bsd; then date_arg='-j -f "%H:%M %p "' else date_arg='-d' fi sunrise=$(date ${date_arg}"$(echo "$weather_data" | "$gnugrep" "yweather:astronomy" | sed 's/^\(.*\)sunset.*/\1/' | sed 's/^.*sunrise="\(.*m\)".*/\1/')" +%H%M) sunset=$(date ${date_arg}"$(echo "$weather_data" | "$gnugrep" "yweather:astronomy" | sed 's/^.*sunset="\(.*m\)".*/\1/')" +%H%M) elif [ -f "${tmp_file}" ]; then __read_tmp_file fi fi if [ -n "$degree" ]; then if [ "$WEATHER_UNIT" == "k" ]; then degree=$(echo "${degree} + 273.15" | bc) fi condition_symbol=$(__get_weather_image "$condition" "$sunrise" "$sunset") echo "${condition_symbol} ${degree}°$(echo "$WEATHER_UNIT" | tr '[:lower:]' '[:upper:]')" | tee "${tmp_file}" fi } # Get symbol for condition. # Available conditions: http://developer.yahoo.com/weather/#codes __get_weather_image() { local condition=$(echo "$1" | tr '[:upper:]' '[:lower:]') local sunrise="$2" local sunset="$3" case "$condition" in "tornado" | "tropical storm" | "hurricane") echo "" # weather_hurricane ;; "sunny" | "fair") time_forecast=$(date +%H%M) if [ "$time_forecast" -ge "$sunset" -o "$time_forecast" -le "$sunrise" ]; then echo "" else echo "滛" # mdi_weather_sunny fi ;; "hot") time_forecast=$(date +%H%M) if [ "$time_forecast" -ge "$sunset" -o "$time_forecast" -le "$sunrise" ]; then echo "" else echo "" # weather_hot fi ;; "rain" | "light rain" | "drizzle" | "light drizzle") echo "" ;; "showers" | "scattered showers") echo "" # weather_showers ;; "mixed rain and snow" | "mixed rain and sleet" | "freezing drizzle" | "freezing rain" | "mixed rain and hail" | "rain and snow") echo "" # weather_rain_mix ;; "light rain with thunder") echo "朗" # mdi_weather_lightning ;; "snow" | "mixed snow and sleet" | "snow flurries" | "light snow showers" | "blowing snow" | "sleet" | "heavy snow" | "scattered snow showers" | "snow showers" | "light snow" | "snow grains") echo "" ;; "hail") echo "晴" # mdi_weather_hail ;; "cloudy" | "mostly cloudy") echo "" # fa_cloud ;; "partly cloudy") echo "杖" # mdi_weather_partlycloudy ;; "severe thunderstorms" | "thunderstorms" | "isolated thunderstorms" | "scattered thunderstorms" | "isolated thundershowers" | "thundershowers") echo "" # weather_lightning ;; "dust" | "fog" | "haze" | "smoky") echo "" # weather_dust ;; "fog" | "foggy" | "mist") echo "" # weather_fog ;; "windy" | "blustery" | "breezy") echo "煮" # mdi_weather_windy ;; "clear" | "cold") time_forecast=$(date +%H%M) if [ "$time_forecast" -ge "$sunset" -o "$time_forecast" -le "$sunrise" ]; then echo "望" # mdi_weather_night else echo "" fi ;; "not available") echo "" ;; *) echo "" # unknown ;; esac } __read_tmp_file() { if [ ! -f "$tmp_file" ]; then return fi cat "${tmp_file}" exit } # exec __run_weather
特に天気情報を表示するスクリプトは更新の止まっているプラグインが多かったので,書いてみて勉強にもなりました.
おわりに
今までの一連の設定を終えるとこんな感じにCUI作業環境が整います.
一からpowerlineもどきを作って,中々自分好みのtmuxを仕上げることが出来たと思う. pic.twitter.com/PnYk3cfmRp
— さわだ.@こたつ (@takuzoo3868) 2018年11月25日
tmuxp使ってあとは,チャットと作業用ウィンドウが起動するようにした. pic.twitter.com/fbjY0Wvi4Z
— さわだ.@こたつ (@takuzoo3868) 2018年11月25日
これらの環境で必要な設定ファイルは全て自分のdotfilesに入っていますので良かったら使ってみてください.
今後はタスク管理やメール管理もCUIで整備したいと考えています.ちなみにIRCでtwitter環境やslackのTUI版であるsclackの導入も考えましたが,うまく出来なかったので知見ある方はコメントで教えていただけると助かります!それでは良きCUIライフを!