【fish】rmコマンドで削除したいファイルをゴミ箱に移動させる

# はじめに
この記事は「[HTC Advent Calendar 2020](https://qiita.com/advent-calendar/2020/htc)」の21日目の記事です。

今回はfishについて書いていこうと思います。([fish公式サイト](https://fishshell.com/))
fishはshellの１つであり、「the friendly interactive shell」の略称です。
私はあまりshellに詳しいわけではないですが、fishを利用している理由はサジェスチョン機能が強力だからです。
めちゃくちゃfishが好きというわけでもないですし、bash、 zeshに精通してるわけでもないです。

過去にrmコマンドで大失敗をした経験がるのでrmコマンドをそのままにしておくのは危険だと思い、自作関数を作ってみることにしました。

# fishとは
あまり細かいことは説明しないので、興味のある方は[fishの公式サイト](https://fishshell.com/)をご確認ください。
ちなみに、公式サイトには「fish is a smart and user-friendly command line shell」と記載されています。
なにを持ってユーザーフレンドリーなのかはよくわかりませんが、オートサジェスチョン機能はとても便利だと、fishを使っていて思っています。

# 結論
あまり説明する内容もないので、最初に結論を書きます。
fishを使っていて、rmコマンドで指定したファイルをゴミ箱に移動させたいという方は、下記のコードをコピペしたら動きます！動くはずです。。。動かなかったら申し訳ありません。
※fish限定なので、bashなどでは利用できません。その辺はご容赦ください。

使用方法としては`~/.config/fish/functions`に`rm.fish`というファイルを作成して、下記のコードを貼り付けるだけです。

```shell:~/.config/fish/functions/rm.fish
function rm -d"send to trash"
    # 削除したいファイルを取得
    set trashfile (string split / $argv)[-1]
    set backupnum 1
    set backupfile (string join "" $trashfile ".bak~" $backupnum)
    # ファイルが既にゴミ箱にある場合ループ処理
    while test -f $HOME/.Trash/$backupfile
        # 1世代分更新
        set backupnum (math $backupnum + 1)
        set backupfile (string join "" $trashfile ".bak~" $backupnum)
    end
    set trashfilepath (string join "" $HOME/.Trash/ $backupfile)
    mv $argv $trashfilepath
    echo '【DONE】'$argv 'send to trash'
end
```

# 内容説明

`function <関数名>`とすることでコンソール上で作成した関数名で呼び出すことができます。今回は`rm`という関数名をつけています。
`-d"send to trash"`は関数の詳細を記載しておくことができます。`-d`は`--description`の略です。
 
```shell:~/.config/fish/functions/rm.fish
function rm -d"send to trash"
// ... 略
end
```

関数に渡した引数は`$argv`で取り出すことができます。
`set trashfile`で変数を宣言しています。

fishでは文字列操作も簡単に行うことができます。
`string split <区切り文字> <文字列>`とすることで、各文字列を指定した区切り文字列で分割することができます。
ここではパスを分割し、リストの最後を指定することで、削除したいファイルだけ取得しています。
例えば、`$args`が`hoge/huga/trash.txt`のような値だった場合、`trash.txt`の部分だけを取り出すことができます。

```shell:~/.config/fish/functions/rm.fish
    set trashfile (string split / $argv)[-1]
```

先ほどは`string split`で文字列の分割を行いましたが、`string join <区切り文字> <文字列>`とすることで、各文字列を指定した区切り文字列で結合することができます。

ゴミ箱に移動するだけでは、同じファイル名の場合に更新されてしまうので、`bak~<世代番号>`としてゴミ箱に移動したあとも世代管理が出来る様にしています。
例えば、`$trashfile`が`trash.txt`だとすると、`trash.txt.bak~1`というようになります。

```shell:~/.config/fish/functions/rm.fish
    set backupnum 1
    set backupfile (string join "" $trashfile ".bak~" $backupnum)
```

fishではもちろんwhile文も利用できます。そして`test -f`でファイルが存在するかの確認を行うことができます。
この場合、削除したいファイルがゴミ箱にあるかを評価しています。ない場合はwhile文で処理が繰り返されます。

`math`を使用することで、文字列を数値として読み取ることができます。
同じファイル名があった場合は世代数をインクリメントし、`string join`を使用してファイル名を変更します。
例えば`trash.txt.bak~1`が既にゴミ箱にある場合は、`trash.txt.bak~2`というようになります。

```shell:~/.config/fish/functions/rm.fish
    # ファイルが既にゴミ箱にある場合ループ処理
    while test -f $HOME/.Trash/$backupfile
        # 1世代分更新
        set backupnum (math $backupnum + 1)
        set backupfile (string join "" $trashfile ".bak~" $backupnum)
    end
```

世代の更新が完了したら`string join`でゴミ箱のパス(`$HOME/.Trash/`)と削除したいファイル名を結合します。
あとはmvコマンドを実行することで、引数として渡したファイルを世代管理した状態でゴミ箱に移動することができます。

```shell:~/.config/fish/functions/rm.fish

    set trashfilepath (string join "" $HOME/.Trash/ $backupfile)
    mv $argv $trashfilepath
    echo '【DONE】'$argv 'send to trash'

```

# まとめ

今回はfishを使って自作関数を作ってみました。
今まではshellって難しそうと思い敬遠していましが、やってみると意外と簡単にできました。（あまり難しいことをしていないからかもしれませんが。。）
これからはrmコマンドで失敗することもありませんし、同じファイルを何度も消してしまっても世代管理をしているので簡単に復元することができます。
ただ、今回のコードでは複数ファイルを引数に渡して削除するということはできない仕様になっているので、機会があればそちらも実装していきたいと思っています。