shellscriptでcp、mvコマンド改造。

久々にシェルスクリプトを書いてみた。
っていってもめっちゃ簡単なものですが、勉強になったのでメモメモ。

やりたいこと

cp file1 file2 ... filen  dir1
mv file1 file2 ... filen  dir1

とファイルをコピー、移動させた後、

cd dir1

そのファイルを編集するため、コピー先のディレクトリに移動することが多い。
が、長いdir1のパスを2回も打つのが面倒なので、これを自動化させたい。


以下、step1〜step5まで長く記述してますが、
step1だけでもだいぶ幸せになれます。
step2は失敗策なので、別に読まなくても大丈夫。

Step1

ヒストリ機能を使えば、簡単に実現。うれしい。
ヒストリの「!$」コマンドは直前に打ったコマンドの最後の引数に置換される。
つまり、

cp file1 file2 ... filen dir1
cd !$

と続けて打つと、!$はdir1に置換されて、あっさりとディレクトリ移動できるというわけです。

ここまではシェルスクリプトじゃなくて、単に!$ていうコマンドがあるよ!って話ですね。

Step2

ええやん。て思うわけですが。 俺は!$て、シフト押しつつ打たなあかん。それさえもめんどくせえ、てなってしまうので、
次なる策です。


発想としては、cd !$て打つのめんどいけど、超便利。 こいつをshellスクリプトで表現→エイリアス化して、もっと簡単なコマンド作って(たとえばcddとか)で代用できないものか!  
って感じですね。
てわけでとりあえず作ってみる


ファイル名:cdd.sh

#!/bin/sh

if [`!!:0` == 'cp' || `!!:0` == 'mv'];then
    cd `!$`
else
    echo "cannot cdd!\n"
fi

直前のコマンド(引数を除く)がcpか、mvだったら、cd !$するってものですね。


fmfm。いけそうだ。 
いけそうなんですが、ヒストリコマンドはあくまでも"置換"であって、"コマンド"ではないみたいで、
上記の用なスクリプトでは!!:0がうまく文字列に置換されないみたいです。


シェルでは

`(バッククォーテーション)
囲まれた部分をコマンドとして実行して結果を返す
'(シングルクォーテーション)
囲まれた部分は完全に文字列とみなされる
"(ダブルクォーテーション)
囲まれた部分は変数を除いて文字列とみなされる。変数($hogeとか)は展開される。

と、クォーテーションにも3つあって、ヒストリコマンドをバッククォーテーションでかこってやってみたんですが、
やはり無理でした。
なんかやり方がありそうな気もしますが、ググれどもググれどもわからないので、断念。
簡単に言うとこの策は失敗です。
なので、やり方知ってる人いたら教えてください。

step3

淡くも失敗に終わったので、別の方向から考えてみる。
cpとcdのコマンドを分けるんじゃなくて、
コピーすると、問答無用にコピー先ディレクトリまで移動するというコマンド(たとえばcppとか)作れば、きっと幸せになれる。


というわけで書いてみる。
ファイル名:cp_cd.sh

#!/bin/sh                                                                  

cp $@ && cd $argv[$#]

1行ですね。w
ただ、これかくのもだいぶ苦労しました。


まず、説明を

cmd1 && cmd2
cmd1が成功したときに限り、cmd2を実行する。
$@
シェルスクリプトを実行したときの引数がすべて格納されている。つまり、./cp_cd.sh file1 file2 file3 dir1 と実行すれば、$@にはfile1 file2 file3 dir1が入る。
$argv[]
引数が配列として格納されている。先の例の場合$argv[1]はfile1。
$#
引数の個数を返す。 先の例の場合、$#は4


1行ですがいっぱいつまってますね。まだペーペーなので全然しらなかったのですが重要みたいです。

step4

さて。できたのでさっそく実行するわけですが、ここにも落とし穴が。
普段シェルスクリプトコマンドライン上で

$ ./hoge.sh

と実行するイメージなのですが、実行後、ディレクトリを移動した状態で終わりたいスクリプトを実行する際は、
$ . hoge.sh
または
$ source hoge.sh
としなければなりませぬ。

というのは、
./hoge.sh:サブシェル上でシェルスクリプトを実行。
. hoge.sh or source hoge.sh:現在のシェル上でシェルスクリプトを実行。
という違いがあるからです。


つまり、前者では動作として、
./hoge.sh実行→サブシェル起動→hoge.shの内容実行(最後にcdで別ディレクトリへ移動)→サブシェル終了→元のシェルに戻る(ディレクトリはもちろん元のまま)
となってしまい、結果として、cdが実行されてないかのようになってしまいます。
なので、cdした状態で終了するシェルスクリプトを実行する場合は、後者のやり方でしなくてはなりません。

step5

あとはパス通して、エイリアスにするだけですね。
作成したシェルスクリプトファイルを適当なディレクトリに置いてパスを通し、
コマンドライン、またシェルの設定ファイル(.zshrcや、.bashrc)に
alias cpp='. ./cp_cd.sh'
とすれば、晴れてどっからでもcppコマンドが使えるようになります。 
もちろんmvも同様にしてmvvとか作れます。   幸せ。