Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

コマンドライン作業を効率化する毎日のBash活用テクニック

Bashは多くのLinuxやUnix系OSで標準シェルとして採用されており、実は使われていない機能が数多く備わっています。そのため、何を取り上げるか決めるのが難しいほどでした。最終的には、「日常業務を楽にしてくれるBashのテクニック」に絞って解説することにしました。

筆者はコンサルタントとして、多種多様な環境や働き方を目にする機会が多くあります。その経験をもとに、テクニックを次の4つのカテゴリに整理しました。ターミナルと行編集の技、ナビゲーションとファイル操作、履歴の活用、そして便利なコマンドです。この分類はあくまで筆者の思考を整理するためのものであり、厳密な区分ではありません。多くのテクニックは複数のカテゴリにまたがるものもあります。

それでは、実際に役立つBashのトリックを紹介していきましょう。

Bash履歴の効果的な使い方

生産性を高める最良の方法のひとつは、Bashの履歴機能を使いこなすことです。特にマルチユーザー環境で最初に行うべき設定のひとつが、histappendオプションの有効化です。以下のコマンドを実行するだけです。

shopt -s histappend 

これにより、複数のターミナルセッションが同時に履歴ファイルへ書き込めるようになります。ほとんどの環境ではこのオプションは無効になっているため、ローカルでもSSHでも、Bashセッションを複数開いていると履歴が失われがちです。

もうひとつの定番タスクが、直前のコマンドをsudo付きで再実行することです。例えば、mkdir /etc/ansible/facts.dを実行したところ、root権限がないため失敗したとしましょう。多くのユーザーは上矢印キーでコマンドを呼び出し、行頭までカーソルを移動してsudoを追加入力します。しかし、もっと簡単な方法があります。

sudo !!

Bashはsudoに続けて直前のコマンド全体を実行します。実際の流れは以下の通りです。

[user@centos ~]$ mkdir -p /etc/ansible/facts.d
mkdir: cannot create directory ‘/etc/ansible’: Permission denied

[user@centos ~]$ sudo !!
sudo mkdir -p /etc/ansible/facts.d

!!が展開されると、実行される完全なコマンドがターミナルに表示されるため、何が実行されたのか一目でわかります。

似た仕組みで使用頻度は低めですが、!*というショートカットもあります。これは「直前のコマンドの引数すべて」を現在のコマンドで再利用したい場合に使います。引数の多いコマンドを流用したいときに便利です。例えば、複数のファイルを作成してからまとめて権限を変更するケースを見てみましょう。

[user@centos tmp]$ touch file1 file2 file3 file4
[user@centos tmp]$ chmod 777 !*
chmod 777 file1 file2 file3 file4

特定の場面限定のテクニックですが、タイピングの手間を確実に減らせます。

キーストローク削減といえば、履歴からコマンドを探す方法にも触れておきましょう。多くのユーザーは次のように検索しがちです。

history |grep <some command>

しかし、もっと手軽な履歴検索の方法があります。ctrl + rを押すと、Bashは履歴を後方検索(逆順検索)します。入力を始めると候補がリアルタイムに表示されます。

(reverse-i-search)`hist': shopt -s histappend

上の例ではhistと入力すると、先ほど紹介したshoptコマンドがヒットしました。さらにctrl + rを押し続けると、他の一致候補を順に遡って検索できます。

最後に紹介するのは厳密にはトリックではなく、履歴の中で最もよく使われているコマンドを集計・表示できる便利なコマンドです。

[user@centos tmp]$ history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head 
81 ssh
50 sudo
46 ls
45 ping
39 cd
29 nvidia-xrun
20 nmap
19 export

この例では、現時点で最も使われているのは圧倒的にsshだということがわかります。

ナビゲーションとファイル名の操作

コマンド名やファイル名、フォルダ名を入力中にtabキーを1回押すと補完されることは、すでにご存じでしょう。これは候補がひとつだけの場合に有効です。しかし、tabを2回押すと、入力済みの文字列に基づく候補がすべて表示されることをご存じない方も多いはずです。

[user@centos tmp]$ cd /lib <tab><tab>
lib/ lib64/

ファイルシステムの移動において非常に便利な機能です。もうひとつ役立つのが、cdspellオプションの有効化です。shopt -s cdspellコマンドを実行すると、タイプミスを自動修正してくれます。

[user@centos etc]$ cd /tpm
/tmp
[user@centos tmp]$ cd /ect
/etc

完璧ではありませんが、小さな助けの積み重ねは大きな快適さにつながります。

ディレクトリ移動後、ひとつ前のディレクトリに戻りたくなることもあるでしょう。浅い階層なら問題ありませんが、/var/lib/flatpak/exports/share/applications/のような深いパスにいる場合は、戻るのに一苦労です。

cd /va<tab>/lib/fla<tab>/ex<tab>/sh<tab>/app<tab>

幸い、Bashは直前のディレクトリを記憶しており、cd -と入力するだけでそこへ戻れます。

[user@centos applications]$ pwd
/var/lib/flatpak/exports/share/applications

[user@centos applications]$ cd /tmp
[user@centos tmp]$ pwd
/tmp

[user@centos tmp]$ cd -
/var/lib/flatpak/exports/share/applications

さらに、頻繁に移動する複数のディレクトリを素早く行き来したい場合もあるでしょう。そんなときは、変数を設定することでナビゲーションを効率化できます。

[user@centos applications]$ export CDPATH='~:/var/log:/etc'
[user@centos applications]$ cd hp
/etc/hp

[user@centos hp]$ cd Downloads
/home/user/Downloads

[user@centos Downloads]$ cd ansible
/etc/ansible

[user@centos Downloads]$ cd journal
/var/log/journal

この例では、ホームディレクトリ(チルダ~)、/var/log/etcを登録しています。これらのディレクトリ直下にあるものは、名前だけで自動的に補完されます。ただし、CDPATHに登録したディレクトリの直下以外は対象になりません。例えば目的が/etc/ansible/facts.d/だった場合、cd facts.dでは到達できません。ansible/etcの下にありますが、facts.dはそうではないからです。つまりCDPATHは、頻繁にアクセスするツリーの最上位への移動には有用ですが、大規模なフォルダ構造を巡回する用途では管理が煩雑になる可能性があります。

最後に、誰もが一度は経験する2つのユースケース、拡張子の変更とファイルの一括リネームについて解説します。一見同じことに思えますが、Bashにはそれぞれ異なるテクニックが用意されています。

泥臭い作業ではありますが、編集中のファイルの簡易コピーが必要になる場面は多々あります。多くの人はファイル名を正確にコピーし、末尾に.old.bakといった拡張子を付け加えるでしょう。例えばspideroak_inotify_db.07pkh3というファイルのコピーを残したい場合、通常はこう打ちます。

cp spideroak_inotify_db.07pkh3 spideroak_inotify_db.07pkh3.bak

コピペやタブ補完、引数再利用のショートカットを使えばそれなりに速くできますが、慣れれば次のコマンドの方がずっと速いことがわかるはずです。

cp spideroak_inotify_db.07pkh3{,.old}

ご想像の通り、これはファイル名の末尾に.oldを付けてコピーするコマンドです。「大量のファイルを一度にリネームしたい」というニーズもあるでしょう。もちろんforループを書いて対応できます(筆者も複雑な処理ではよく書きます)が、renameという便利なユーティリティがあるのにわざわざ書く必要はありません。ただし、Debian/Ubuntu系とCentOS/Arch系では使い方が異なる点に注意してください。Debian系のrenameはSED風の構文を使います。

user@ubuntu-1604:/tmp$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

user@ubuntu-1604:/tmp$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

user@ubuntu-1604:/tmp$ rename 's/old_text_file/shiney_new_doc/' *.txt

user@ubuntu-1604:/tmp$ ls shiney_new_doc_*
shiney_new_doc_1.txt shiney_new_doc_3.txt shiney_new_doc_5.txt
shiney_new_doc_2.txt shiney_new_doc_4.txt

CentOSやArchでは次のようになります。

[user@centos /tmp]$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

[user@centos /tmp]$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

[user@centos tmp]$ rename old_text_file centos_new_doc *.txt

[user@centos tmp]$ ls centos_new_doc_*
centos_new_doc_1.txt centos_new_doc_3.txt centos_new_doc_5.txt
centos_new_doc_2.txt centos_new_doc_4.txt

Bashのキーバインド

Bashには多数のキーボードショートカットが組み込まれています。bind -pと入力すれば一覧を確認できます。有名どころも含まれますが、いくつか厳選して紹介します。

  • ctrl + _(取り消し)
  • ctrl + t(2文字を入れ替え)
  • ALT + t(2単語を入れ替え)
  • ALT + .(直前のコマンドの最後の引数を表示)
  • ctrl + x + *(グロブ/アスタリスクを展開)
  • ctrl + 矢印キー(1単語前進)
  • ALT + f(1単語前進)
  • ALT + b(1単語後退)
  • ctrl + xctrl + e(コマンド文字列をエディタで開き、実行前に編集できる)
  • ctrl + e(カーソルを行末へ)
  • ctrl + a(カーソルを行頭へ)
  • ctrl + xx(行頭と行末を往復)
  • ctrl + u(カーソルより前をカット)
  • ctrl + k(カーソルより後ろをカット)
  • ctrl + y(バッファからペースト)
  • ctrl + l(小文字のL。画面クリア)

自明なものは割愛しますが、筆者が特に有用だと感じているのは、単語(またはテキストの一部)を削除して元に戻せるショートカット群です。例えば、systemdで多数のサービスを停止しようとしたけれど、ある処理の完了後には一部だけ起動したい、という状況を考えてみましょう。

systemctl stop httpd mariadb nfs smbd
<上矢印で直前のコマンドを呼び出す>
<'ctrl + w'で不要な引数を削除>

ここで、削除しすぎてしまったらどうすればいいでしょうか?心配無用です。ctrl + _で直前の編集を取り消せます。

その他のカット系コマンド(Ctrl + kCtrl + u)を使えば、カーソル位置から行末または行頭までを一括で削除できます。さらに嬉しいことに、カットしたテキストはターミナルのバッファに入るため、後からctrl + yで貼り付け可能です。これらのコマンドは文章での説明が難しいので、ぜひ自分の手で試してみてください。

最後に、コンテナのような制約の多い環境で非常に役立つ、あまり知られていないキーの組み合わせを紹介します。以前の出力によってコマンド表示が崩れてしまった場合の解決策がこれです。ctrl + x + ctrl + eを押すと、環境変数EDITORに設定されたエディタでコマンドが開かれます。長いコマンドや崩れたコマンドを、テキスト折り返し(可能な)エディタで編集できるわけです。通常のファイルと同様に保存して終了すると、エディタを抜けた時点でコマンドが実行されます。

その他の便利なヒント

Bashシェルに色が付くと、作業体験がぐっと向上します。色が有効になっていないセッションを使っている場合は、以下のコマンド群を.bash_profileに追加してみてください。内容はかなり素直なので、詳細な説明は不要でしょう。

# enable colors
eval "`dircolors -b`"

# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'

# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'

# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

# Add some colour to LESS/MAN pages
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;42;30m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

Bashの各種オプション調整に加えて、時間短縮につながる小技もあります。例えば、終了ステータスに関係なく2つのコマンドを連続実行したい場合は、;で区切ります。

[user@centos /tmp]$ du -hsc * ; df -h

これは、カレントディレクトリ内の各ファイルの容量を計算して合計を出し、続けてブロックデバイスごとのディスク使用量を照会します。duコマンドがエラーを返しても、後続のコマンドは必ず実行されます。

では、最初のコマンドが成功した場合のみ次のアクションを実行したい場合はどうでしょう?&&を使えば、最初のコマンドが正常な終了ステータスを返したときだけ2番目のコマンドが実行されます。例えば、アップデートが成功した場合にのみマシンを再起動したい場合はこう書きます。

[root@arch ~]$ pacman -Syu --noconfirm && reboot

コマンド実行時に、その出力を記録したいこともあるでしょう。teeコマンドの存在は多くの方がご存じでしょう。標準出力をターミナルとファイルの両方に複製してくれるコマンドです。しかし、straceのような複雑な出力を捕捉したい場合は、I/Oリダイレクトの知識が必要になります。I/Oリダイレクトの詳細は本稿の範囲を超えますが、ここではSTDOUTSTDERRに注目します。目にしている出力を正確に記録する最善の方法は、両者をひとつのファイルにまとめることです。そのために2>&1リダイレクトを使用します。

[root@arch ~]$ strace -p 1140 > strace_output.txt 2>&1

これで関連する出力すべてがstrace_output.txtに保存され、後から確認できます。

長時間実行されるコマンドの途中で、処理を一時停止したくなることがあります。ctrl + zの「ストップ」ショートカットを使えば、ジョブを(強制終了せずに)停止できます。ジョブはジョブキューに追加されますが、再開するまでは表示されません。後からフォアグラウンドコマンドfgで再開できます。

また、ctrl + sでジョブを単純に一時停止することもできます。この場合、ジョブとその出力はターミナルのフォアグラウンドに留まり、シェルの操作権限はユーザーに戻りません。ctrl + qを押せば再開します。

GUI環境で多数のターミナルを開いているなら、出力のコピペ用ショートカットがあると便利です。以下の組み合わせを使います。

# Copies highlighted text
ctrl + shift + c

# Pastes text in buffer
ctrl + shift + v

実行中のコマンドの出力に別のコマンドが登場し、その詳細を知りたくなったとしましょう。方法はいくつかあります。そのコマンドがパス上に存在するなら、whichコマンドでディスク上の配置場所を特定できます。

[root@arch ~]$ which ls
/usr/bin/ls

この情報をもとに、fileコマンドでバイナリを調査できます。

[root@arch ~]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d4e02b88e596e4f82c6cc62a5bc4ce5827209a49, stripped

さまざまな情報が表示されますが、多くのユーザーにとって重要なのはELF 64-bit LSBの部分です。これは、スクリプトなどではなくプリコンパイルされたバイナリであることを意味します。コマンドを調査する関連ツールとして、commandコマンドそのものも使えます。command -V <command>を実行すると、コマンドの種類に応じた情報が得られます。

[root@arch ~]$ command -V ls
ls is aliased to `ls --color=auto`

[root@arch ~]$ command -V bash
bash is /usr/bin/bash

[root@arch ~]$ command -V shopt
shopt is a shell builtin

最後に、筆者のお気に入りのテクニックのひとつ、echoコマンドを紹介します。特にコンテナや、把握も制御もままならない環境で作業するときに威力を発揮します。forループが期待通りのシーケンスで回るかの事前確認から、リモートポートの開放チェックまで、幅広く使えるコマンドです。ポートのオープン確認の構文は非常にシンプルで、echo > /dev/<udp or tcp>/<server ip>/<port>と書きます。

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/222
-bash: connect: Connection refused
-bash: /dev/tcp/192.168.99.99/222: Connection refused

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/22

接続先のポートが閉じていればConnection refusedメッセージが返り、パケットの送信に成功すれば何も出力されません。

これらのテクニックが、Bashをより効率的かつ楽しく使う助けになれば幸いです。本稿で挙げた以外にも、Bashにはまだまだ隠れたトリックが眠っています。あなたのお気に入りのテクニックは何ですか?

付録1. 本稿で扱ったTips一覧

# History related 
ctrl + r (reverse search)
!! (rerun last command)
!* (reuse arguments from previous command)
!$ (use last argument of last command)
shopt -s histappend (allow multiple terminals to write to the history file)
history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head (list the most used history commands)

# File and navigation
cp /home/foo/realllylongname.cpp{,-old}
cd -
rename 's/text_to_find/been_renamed/' *.txt
export CDPATH='/var/log:~' (variable is used with the cd built-in.)

# Colourize bash

# enable colors
eval "`dircolors -b`"
# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'
# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'
# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m' # end the info box
export LESS_TERMCAP_so=$'\E[01;42;30m' # begin the info box
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

# Bash shortcuts
shopt -s cdspell (corrects typoos)
ctrl + _ (undo)
ctrl + arrow (move forward a word)
ctrl + a (move cursor to start)
ctrl + e (move cursor to end)
ctrl + k (cuts everything after the cursor)
ctrl + l (clears screen)
ctrl + q (resume command that is in the foreground)
ctrl + s (pause a long running command in the foreground)
ctrl + t (swap two characters)
ctrl + u (cuts everything before the cursor)
ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before it runs)
ctrl + x + * (expand glob/star)
ctrl + xx (move to the opposite end of the line)
ctrl + y (pastes from the buffer)
ctrl + shift + c/v (copy/paste into terminal)

# Running commands in sequence
&& (run second command if the first is successful)
; (run second command regardless of success of first one)

# Redirecting I/O
2>&1 (redirect stdout and stderr to a file)

# check for open ports
echo > /dev/tcp/<server ip>/<port>
`` (use back ticks to shell out)

# Examine executable
which <command>
file <path/to/file>
command -V <some command binary> (tells you whether <some binary> is a built-in, binary or alias)
  1. Linuxで効果的なBashスクリプトを書くための実践テクニック10選

    シェルスクリプトは、Linuxで学べる最も手軽なプログラミング手法のひとつです。システム管理におけるタスクの自動化や、簡単なユーティリティ・ツールの開発など、管理者にとって必須のスキルとも言えます。 本記事では、効果的かつ信頼性の高いbashスクリプトを書くために役立つ、実用的な10のヒントを紹介します。 1. スクリプトには必ずコメントを書く これはシェルスクリプトに限らず、あらゆるプログラミングで推奨される基本プラクティスです。コメントを適切に記述しておけば、後から自分や他の人がスクリプトを読んだときに、各部分が何をしているのかを素早く理解できます。 コメントは # 記号で定義します。 #

  2. 楽しく学べる!Bash習得に役立つコマンドラインゲーム3選

    学習は大変な作業であり、誰も「作業」そのものは好きではありません。つまり、Bashがどれだけ習得しやすい言語だとしても、やはり「勉強」という感覚を拭いきれないかもしれません。しかし、ゲームを通して学ぶなら話は別です。 ターミナルの使い方を教えてくれるゲームなど、そう多くは存在しないと思うでしょう。実際、その通りなのです。PCゲーマーならご存知の通り、FalloutシリーズにはVault内にターミナル式のコンピュータが登場し、テキストでコンピュータと対話するという概念に自然と親しめるようになっています。AlpineやEmacsに似たアプリケーションも登場しますが、残念ながらFalloutをプレイ