heyheytower

日々のメモです。誰かのお役に立てれば幸いです。

`bash` でディレクトを移動した時と、コマンドが空の状態で Enter を押したときに `ls` と `git status` を表示する (よく使うコマンド操作の簡略化・自動化)

コマンド操作の簡略化・自動化した結果のスクリーンショット
Pic.1 各種トリガーでls -AFCgit status -sb が実行されたところ

目次

目的

コマンド履歴を確認したところ、いつも同じようなコマンドを実行していることが分かった。
操作の簡略化・自動化できる要素が多分にあったため、その対応を行う。

Op.1 環境確認

$ cat /etc/debian_version
stretch/sid
$ uname -a
Linux calc0 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"

コマンド履歴の確認

コマンド履歴の達人を目指してみる - ザリガニが見ていた…。のコマンドを参考にさせていただきました。

Op.2 実行頻度の高い上位10コマンド

$ history|sed -e 's/sudo //g'|awk '{ print $2 }'|sort|uniq -c|sort -r|head
   1181 ll
    918 vi
    619 cd
    534 git
    245 apt
    226 l
    211 systemctl
    165 journalctl
    133 rm
    129 cat

方法

できるだけキー入力を省略できるような仕組みと、定期実行でコマンド自動化(cron等)を行う。

bash でコマンドが空の状態で Enter を押したときに lsgit status を表示する

下記の仕組みを用いれば、lsgitの入力を減らすことができると考えた。

コマンドが空であることの判定は、~/.bash_historyの行数の増加をチェックして用いることにした。
当該コードについては次のトピックが関連するので、次のセクションのコードに併記します。

ディレクトリを移動したら自動で lsgit status を表示する

下記を参照しました。

本件は PROMPT_COMMAND を用いることで複数のコマンド実行を実現しており、それを dispatch にて処理するために下記を参照しました。

そして、前述の内容のコードを合わせた下記のコードを.bashrcに追記します。

Code.1 bashディレクトを移動した時と、コマンドが空の状態で Enter を押したときに lsgit status を表示する

# Git repository check
function check_git {
    if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = 'true' ]; then
        echo -e "\e[0;33m--- git status ---\e[0m"
        git status -sb
    fi
}

# cd -> cd + ls
function autols {
    if [ "${AUTOLS_DIR:-$PWD}" != "$PWD" ]; then
        ls -ACF
        check_git
    fi
    AUTOLS_DIR="${PWD}"
}
export PROMPT_COMMAND_AUTOLS="autols"

# Enter -> ls + git status
export HISTCONTROL=ignorespace
COUNT=$(wc -l < ~/.bash_history)
function lsgit {
    COUNT_TMP=$(wc -l < ~/.bash_history)
    if [ "$COUNT" != "$COUNT_TMP" ]; then
        COUNT="$COUNT_TMP"
        return 0
    fi
    
    ls -ACF
    check_git
}
export PROMPT_COMMAND_LSGIT="lsgit"

dispatch () {
    export EXIT_STATUS="$?"

    local f
    # shellcheck disable=SC2153
    for f in ${!PROMPT_COMMAND_*}; do
        eval "${!f}"
    done
    unset f
}
export PROMPT_COMMAND="dispatch"

挙動

まず history の挙動について下記を一読ください。

・先頭がスペースであるコマンドを履歴に残さない方法 > ※はてなブックマークでの補足コメント、有難う御座いました。

1 > export HISTCONTROL=ignorespace
・先頭がスペースであるコマンドを履歴に残さない+重複コマンドを記録しない方法

1 > export HISTCONTROL=ignoreboth

Linuxでコマンド履歴を残さない方法 | 十円玉という名のブログ

~/.bashrcに記載があるのですが、デフォルトではHISTCONTROL=ignorebothの設定です。

今回コード中でexport HISTCONTROL=ignorespaceを指定しており、その点をふまえて本件の挙動を確認してみます。

トップに置いてあるスクリーンショットをここで再掲しますので、合わせて挙動を確認してみてください。

Enter押下時のスクリーンショット

  1. cd ~/.github/を実行する(cd入力がないが、省略する設定を行っている。後述。)
  2. ディレクトリを移動したら自動でlsgit status を表示するgitリポジトリではないのでstatus表示はなし)
  3. cd dotfilesを実行する
  4. ディレクトリを移動したら自動で lsgit status を表示する
  5. wコマンドの実行
  6. wコマンドの結果表示
  7. コマンドが空の状態で Enter を押す
  8. ~/.bash_historyの行数が増えなかったため、lsgit status を表示する
  9. cd .gitを実行する
  10. ディレクトリを移動したら自動で lsgit status を表示する(gitリポジトリではないのでstatus表示はなし)
  11. cd ..の実行
  12. ディレクトリを移動したら自動で lsgit status を表示する
  13. cd ..の実行
  14. ディレクトリを移動したら自動で lsgit status を表示する(注意:HISTCONTROL=ignorebothの条件では、同じコマンドを続けて実行した際は~/.bash_historyの行数が増えないため、lsgit statusが表示されるという動きになってしまい、ここでは"ディレクトリを移動したら"もTRUEなので2回lsgit statusが表示されてしまいます。)

※どこのディレクトリでもgitリポジトリか確認してgit statusを表示するという手もありますが、リポジトリ内でコマンド実行のたびにgit statusが表示されるのは可読性が悪くなると思い、上記のような挙動をコーディングしました。

bash でのcd入力の省略

~/.bashrcshopt -s autocdを追記すると、ディレクトリ名のみが入力された際にcdが自動追記されます。

apt の定期実行

下記2サイトを参考にしました。

3 unattended-upgradesパッケージ
自動アップグレードを実行するパッケージです。
デフォルトだとセキュリティアップデートのみをアップグレードする設定になっています。
アップデート後にシステム再起動が必要な場合に再起動させることも設定できます。
サーバ運用している場合はアップグレード作業が不要になるので便利なツールです。

Ubuntu 16.04: 自動アップデート / アップグレードの設定をする - Narrow Escape

自分の環境ではすでにインストールされていることが確認できたので、/etc/apt/apt.conf.d/20auto-upgradesの内容を確認して、足りない内容を追記しました。(自動実行自体は過去に"ソフトウェアとアップデート"で設定していたのか、インストール時に設定していたのかできていたよう…)

Op.3 aptの自動実行設定の追記

$ sudo apt -y install unattended-upgrades
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
unattended-upgrades はすでに最新バージョン (0.90ubuntu0.3) です。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。
$ echo 'APT::Periodic::AutocleanInterval "1";' | sudo tee -a /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::AutocleanInterval "1";
$ echo 'APT::Periodic::Download-Upgradeable-Packages "1";' | sudo tee -a /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Download-Upgradeable-Packages "1";

(2017/1/23 追記)
/etc/apt.conf.d/50unattended-upgradesに対して、アップグレード対象をセキュリティだけでなく通常のアップデートも含めるためのコメントインと、更に自分はPPAから外部パッケージのNeovimをインストールしていたので下記を参照にしてレポジトリの追加を行った。

$ head /var/lib/apt/lists/ppa.launchpad.net_neovim-ppa_unstable_ubuntu_dists_xenial_InRelease 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Origin: LP-PPA-neovim-ppa-unstable
Label: Neovim Unstable
Suite: xenial
Version: 16.04
Codename: xenial
Date: Sun, 22 Jan 2017 20:50:44 UTC
Architectures: amd64 arm64 armhf i386 powerpc ppc64el s390x
$ 
$ head /etc/apt.conf.d/50unattended-upgrades
// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
        "${distro_id}:${distro_codename}";
        "${distro_id}:${distro_codename}-security";
        "${distro_id}:${distro_codename}-updates";
//      "${distro_id}:${distro_codename}-proposed";
//      "${distro_id}:${distro_codename}-backports";
        "LP-PPA-neovim-ppa-unstable:xenial";
};
$ sudo mv /etc/cron.daily/apt-compat /etc/cron.daily/.apt-compat

cronによるコマンドの自動実行

Saba noteを参考に、いつもaptと一緒に実行していた下記のコマンドを一日一回は実行するように設定しました。

$ crontab -l
# min hour day mth wday user command
10 23 * * * ito trash-empty 30 >/dev/null 2>&1
20 23 * * * ito nvim -c "call dein#update()|q" >/dev/null 2>&1

本環境でのファイル削除は、rmの代わりにtrashコマンドを用いています。(CLI削除の場合もゴミ箱に投入され、削除して30日経過したファイルをゴミ箱からも削除する設定にしています。)

Neovim プラグインのアップデートも一日一回できるようになりました。(プラグインアップデート関数の実行を引数として与え、実行後にNeovimを終了しています。)

暗黒美夢王(deo developer)(@ShougoMatsu)さん、いつもありがとうございます!!

まとめ

目的は達成できた。

これでコマンド履歴の確認のトップ10のうち、下記のコマンドの入力回数が減ることが期待できます。

  • ll
  • cd
  • git
  • apt
  • l

以上