プログラミング
シェルの感嘆符は叫ぶためではない。怠惰であれ。
A shell exclamation mark is not for yelling. Be lazy. (refp.se)
要約
この記事は、シェルの感嘆符(!)を使ったコマンドラインの繰り返しを効率化する方法について解説しています。イベント指定子、ワード指定子、モディファイアといった機能を使うことで、過去のコマンドやその引数を参照し、入力の手間を省くことができます。また、POSIX標準のfcコマンドも紹介し、コマンドライン操作の効率化と「Don't Repeat Yourself(DRY)」の原則をシェルでも適用することの重要性を説いています。
全文翻訳
シェルの感嘆符は叫ぶためではない。怠惰であれ。
イベント指定子は1970年代後半から、それほど隠されることなく存在しており、開発者のキーボードを消耗させるための大規模な陰謀の一部であるかのように強力です。
コマンドラインの繰り返しを社会的に受け入れていること
繰り返すな。
これは何というマジック、イベント指定子?
イベント指定子
ワード指定子
モディファイア
POSIXとfcの力
責任を持って叫べ
3の法則
強力な4つ
まだ感嘆符の使い方が自信ない、何かアドバイスは?
さて、これからどうする?
よくある質問
なぜCtrl-Rを使わないのか、たくさんのことを覚える代わりに?
なぜ!$の代わりにAlt-.を使わないのか?
他の便利な機能を無視していないか?!邪魔になるだけだが、どうすればオフにできる?
さらに読む
同僚に叫ぶな。シェルで叫べ
あなたと私はおそらく非常に異なっており、私のエディタの選択はあなたが考えるだけで顔をしかめる可能性が高く、あなたの好きなプログラミング言語は統計的に見て、間違いなく私のそれではありません。しかし、私たち全員に共通する一つのこと(そしておそらく唯一のこと)があります。それは、私たち全員が怠惰なクソ野郎であるということです。
注: この記事は主にbash、csh、tcsh、zshを対象としています。これらのシェルがあなたの日常的なドライバーでない場合は、POSIXとfcの力を見てください。どのPOSIX互換シェルを使用していても読む価値があります。
コマンドラインの繰り返しを社会的に受け入れていること
ssh some-user@10.240.33.109
echo "done with some-user@10.240.33.109" >> ~/work/superfun-client/worklog
cd ~/work/superfun-client && cat TODO
ssh some-user@10.240.33.109 # <- 何か忘れた
私たちは皆、シェル履歴でコマンドを繰り返す際に矢印上キーを連打する地獄に陥ったことがあるはずですが、代わりに手間を省き、以前のコマンドとその引数を直接参照できるとしたらどうでしょうか?
ssh some-user@10.240.33.109
echo "done with !:1" >> ~/work/superfun-client/worklog
cd !$:h && cat TODO
!ssh
叫ぶな — 感嘆符だけだ。
注: この記事で説明されているマジックは、特に設定されていない限り、対話型シェルに適用されます。新しい素晴らしい.shシェルスクリプトで使用するためのものではありません。
繰り返すな。
おそらく以前に聞いたことがあるでしょう、「繰り返すな」と。それは、奇妙な魔法をすべて知っている(そして少し変な匂いがするかもしれない)あの迷惑な同僚からか、あるいはmatrix.orgの連邦遅延よりも長引いた最後のレファクタリングセッション中に自分で言ったのかもしれません。
apt install awesome-package # 権限不足
sudo !! # sudoで繰り返す
mkdir /var/mnt/cache # いつものディレクトリ作成ダンス
cd !$ # 指を曲げずにディレクトリに移動
ほとんどの人は、私たちがジュニアだった頃からDRYを繰り返し聞かされてきました。しかし、私たちがどこでもDRYを適用するように熱心に教えられてきたのに、コマンドラインではそうではないのはおかしいと思いませんか?
touch ~/projects/work/awesome.sh
cd !$:h # awesome.shのディレクトリに移動
ssh 10.240.33.109 -p2222 -i ~/.ssh/prod/ed25519
ssh 10.240.33.110 !:2*
# 同じフラグ、別のホスト
ffmpeg -i /recordings/a-sunny-day.mov !#:2:r.mkv
ffmpeg -i /recordings/a-sunny-day.mov /recordings/a-sunny-day.mkv
scp !$:r.* example.com:/media
scp /recordings/a-sunny-day.* example.com:/media
これは何というマジック、イベント指定子?
一見すると恐ろしく見えるかもしれませんが、イベント指定子は常に同じ比較的単純なパターンに従います: ![event][:word][:modifier]
| | | | | '--> modifier [ :h :t :r :e ... ]
| '-----------> which part [ :0 :$ :* :2-3 ... ]
'------------------> which line [ !! !-2 !ssh !?needle? ... ]
注: 基本的なことを知りたいですか?私が最も有用だと考える必須の指定子については、責任を持って叫べを参照してください。
注: すでに流暢ですか?POSIXとfcの力にスキップしてください。私がこれまでに実行した中で最も有用な、しかしニッチなコマンドの一つです。エディタで長いコマンドを編集できることを願ったことがない人はいますか?
イベント指定子
感嘆符の直後に続く最初の部分は、どの行に興味があるかを示します。以下は、参照されたコマンドを効果的に再実行する例です。
!! # 直前の行
!-2 # 2行前
!1337 # `history`で表示される1337行目
!ssh # "ssh"で始まる直近の行
!?dandelion? # "dandelion"を含む直近の行
!# # 現在入力中の行
^ssh^scp^ # 直前のコマンドの最初のsshをscpに置き換える
注: !の直後に:がある場合、明示的なイベントは指定されず、式は直前の行を参照します。
ワード指定子
イベント指定子の後(もしあれば)、どの部分に関心があるかを指定できます。以下の例はすべて、最初のコードブロックのコマンドの後に続くものとして書かれています。
/path/to/script.sh "hello world" --enable 1337
!:0 # 最初の単語 --> "/path/to/script.sh"
!:1 # 2番目の単語 --> "hello world"
!:$ # 最後の単語 --> "1337"
!:1-2 # 2番目から3番目 --> "hello world" "--enable"
!:* # すべての引数 --> "hello world" "--enable" "1337"
!:1- # 最後以外すべて --> "hello world" "--enable"
!:2* # 3番目から最後 --> "--enable" "1337"
注: コロンを指定しなくても機能するいくつかのショートカット表現があります。例えば、$!は!: $と同等であり、$*は!: *と同等です。
モディファイア
モディファイアはおそらく「オーマイガー」の領域に入る場所です。これらは、それ以外の場合は完全な引数となるものを、特定のパーツだけを抽出/変更することを可能にします。
!:$:h # ファイル名を削除 (`dirname`)
!:1:t # パスを削除 (`basename`)
!:1:r # 拡張子のみを削除
!:1:e # 拡張子のみを残す
!:s/hello/bye/ # 最初の'hello'を'bye'に置き換える
!:gs/foo/bar/ # すべての'foo'を'bar'に置き換える
!ssh:p # 実行せずに、実行されるであろうものを表示する
POSIXとfcの力
あなたのシェルが!を話すかどうかにかかわらず、fcはあなたの想像上のツールベルトに属します。前のコマンドを繰り返すために使用できますが、真の力は、シェルではなく、選択したエディタでコマンドラインを編集できるという事実にあります。すべてのシェル冒険者は、年に少なくとも一度はfcを実行すべきです。その使いやすさに驚嘆するだけでも価値があります。
fc # 直前のコマンドを編集
fc -2 # 2ステップ前のコマンドを編集
fc grep # grepで始まる最後のコマンドを編集
fc -3 -1 # 最後の3つのコマンドをすべて1つのバッファに
fc ssh -1 # 最後の'ssh'行から直近の行まで
fc -s ssh # sshで始まる最後のコマンドを繰り返す
fc -s hello=world ssh # sshで始まる行の"hello"を"world"に置き換える
fcは、$FCEDITで指定されたプログラムを呼び出して編集を行います。そのような指定がない場合、POSIXはedにフォールバックします。多くのシェルは$EDITORをデフォルトとしますが、あなたは-eを使用して呼び出すエディタを指定することもできます。
scp api.tar.gz prod-01:/tmp
ssh prod-01 systemctl restart api
curl -sf https://prod-01/health
fc -e 'sed -i s/prod-01/test-01/g' -3 -1
上記は3つのコマンドすべてを再実行し、各コマンドでprod-01をtest-01に置き換えます。おそらくこじつけの例ですが、そのような問題に遭遇した場合に非常に役立ちます。
注: fcはPOSIXのUser Portability Utilitiesにあります。dashやbusybox ashのような非常にミニマルなシェルには、完全に含まれていません。
責任を持って叫べ
イベント指定子、またはPOSIXの場合はfcは、あなたの人生を変え、あなたを常にパーカーを着た über-10x 開発者に変えるわけではありません。しかし、一度に4から42キーストロークを節約できるかもしれません。週に数百回、キャリア spanning many years の間に。私がエッセンシャルだと考えるものを以下に示します。いくつか覚えておけば、あなたはすでに過去の自分よりもはるかに進んでいます。
3の法則
!! 直前のコマンドに展開される
!needle "needle"で始まる直近のコマンドに展開される
!?needle? "needle"を含む直近のコマンドに一致する