それゆけ!ターミナル部 第3回 シェルの展開を使いこなそう!
skill

それゆけ!ターミナル部 第3回 シェルの展開を使いこなそう!

2016.11.18

みなさんこんにちは。ターミナル部 部長のタナカトモフミです。

本連載では意外と見落としがちな基本的な事柄からちょっとマニアックなテクニックまで、ターミナルを使いこなすための方法を若手エンジニアのタミ夫くんとシェルスキー先生と一緒に学んでいきます。

今回はシェルの展開について学びます。うまく活用すればファイルの操作などをまとめて行うことができるようになるので、是非マスターしてください!

タナカトモフミ

タミ夫くんは今日も元気にキーボードを連打しています。
いったい何をしているのでしょうか?

カタカタカタカタカタカッターン!
カタカタカタカタカタカッターン!カチャパーン!

おいおいおい。なんでまたそんな連打しとるんじゃ?

ちょっとファイルのバックアップとってて、ファイル名の後ろ変えるだけでも結構打ちません?

シェルの展開を使えば、そんな打たなくてもできるじゃろ?

なんスか?それ? *.txt とかのやつっスか?そういうの早く言ってくださいよー!

よく使われるのはそれじゃが、それ以外に便利に使えるのがたくさんあるんじゃ。
どうも基本からやったほうが良さそうじゃな。

基本を身につけてタイプを減らそう

まずはシェルの基本中の基本である * についておさらいします。

テキストファイル の一覧が見たい場合に以下のようなコマンドを打ったことがあると思います。

$ ls *.txt

この * は、ワイルドカードと呼ばれ、シェルによって解釈され、その結果がプログラムの引数として渡されます。

例えば現在のディレクトリ内に a.txt, b.txt, c.txt, d.py といったファイルが存在する場合、以下を実行した場合と同じになります。

# こんなファイルがある場合
$ ls
a.txt, b.txt, c.txt, d.py

# * ワイルドカードを使用した場合
$ ls *.txt
a.txt b.txt c.txt

# ワイルドカードと同じ意味
$ ls a.txt b.txt c.txt
a.txt b.txt c.txt

プログラム側に *.txt が引数として渡されるわけではない点に注意しましょう。どのように解釈されてプログラムに渡されるのかを、echo コマンドを使って確認する方法もあります。

$ echo ls *.txt
ls a.txt b.txt c.txt

上記のように ls コマンドに 3つの txtファイルが渡されるように展開されています。

? を使ってみよう

? もワイルドカードの1つです。 ? を使えば、任意の1文字をパターンマッチすることができます。

$ ls
a1.txt a2.txt, a3.txt, b.txt, c.py

# aの後ろに1文字なんでもあって、.txtが続くファイル名
$ ls a?.txt
a1.txt a2.txt a3.txt

これを使用するとファイル名の長さでマッチもできます。

$ ls
a.txt aa.txt aaa.txt c.py

# .txt 拡張子の前が1文字のファイルを指定
$ ls ?.txt
a.txt

# .txt の拡張子の前が2文字のファイルを指定
$ ls ??.txt
aa.txt

# ファイル名が6文字以上のファイルを指定
$ ls ??????*
aa.txt aaa.txt

# 拡張子が 2文字のファイルを指定
$ ls *.??
c.py

? を何個も使う方法は、実際にはそれほど利用頻度は高くありませんが、覚えておけば重宝します。

ブラケットは超便利

さらに便利なのが、ブラケットを使った指定方法です。

ブラケットは [] (Square Brackets) と {} (Curly Brackets) の2種類があります。まずは [] の使い方を見てみましょう。

$ ls
a1.txt a2.txt a3.txt a4.txt b1.txt b2.txt b3.txt c1.txt

# aかc の後に 1.txt と続くもの
$ ls [ac]1.txt
a1.txt c1.txt

# 1から3の連番指定
$ ls a[1-3].txt
a1.txt a2.txt a3.txt

# aからcのアルファベット並び指定
$ ls [a-c]1.txt
a1.txt b1.txt c1.txt

# さらに組み合わせてみる
$ ls [a-b][2-3].txt
a2.txt a3.txt b2.txt b3.txt

このように [] を使用することで 範囲を簡単に指定することができます。

次に、{} の使い方を見てみましょう。

$ ls
a1.txt a2.txt a3.txt a4.txt b1.txt b2.txt b3.txt c1.txt

# aかc の後ろに 1.txt と続くもの
# ls [ac]1.txt と同じ
$ ls {a,c}1.txt
a1.txt c1.txt

# 1文字や範囲ではなく、連続する文字列を複数指定できる
$ ls {a1,a2,b1}.txt
a1.txt a2.txt b1.txt

ここで注意すべきことがあります。

*、?、[] を使用すると、実際に条件にマッチするファイルが存在する場合に、そのファイルを引数として渡すようになっています。それに対して、{} は、実際にファイルが存在するかとは関係なく、単純に指定したパターンが展開されます。

実際に条件にマッチするファイルに絞り込んで展開する *、?、[] と、ファイルの存在にかかわらず展開する {} という違いがある点に注意しましょう。

単純に展開される {} は、空ファイル/ディレクトリの作成や、ファイル名の末尾を少し変えてコピーを取るなどの操作に使用することができます。コピーや移動コマンドは、うっかり間違うと悲惨なので、展開された結果が意図しないものになっていないか echo コマンドを使って事前に確認すると良いでしょう。

# まとめて空ファイルを作成する
$ echo touch {aa,bb,cc,dd}.txt
touch aa.txt bb.txt cc.txt dd.txt

# ファイル名の末尾に文字を追加するコピー
# カレントのファイル名が長い場合や、ファイル名だけでなく、パスも書く場合など、同じ文字を2回書く手間を省ける
$ cp path/to/a.txt{,.bk}
cp path/to/a.txt path/to/a.txt.bk

# date コマンドと組み合わせて日付つきのコピーを取る
$ echo cp a1.txt{,.bk.$(date '+%Y%m%d')}
cp a1.txt a1.txt.bk.20161013

# mv コマンドでファイル名のリネームをする
$ echo mv path/to/{a,b}.txt
mv path/to/a.txt path/to/b.txt

# ディレクトリをまとめて作りたい
# {} を2回 使うこともできる。その場合は組み合わせが展開されるので、この場合は4つの値に展開される
$ echo mkdir –p src/{main,test}/{scala,java}/com/example
mkdir -p src/main/scala/com/example src/main/java/com/example src/test/scala/com/example src/test/java/com/example

まとめ

いやーなるほどっスねー。これならタイプする数めっちゃ減らせますわ。

そうじゃな。覚えておくと結構便利じゃよ。

もう、ほんと早く言えよって感じっスわー。
ただこれコマンド書いたときに、あってるか自信ないこと多そうなんで mv とかするときほんと怖いっスねー。

君にも怖いとかあるんじゃな。意外じゃわ。
そういうときは echo コマンドで試してからってのも覚えておくんじゃぞ。

いやいや意外とか失礼っスわー。
こう見えて慎重派なんでそこんとこよろしくっス。

お、おう。(君の方がわしに失礼な発言多くないかのぅ。。。)

シェルの展開は便利ですが、失敗した場合の被害も大きいので、シェルスキー先生の言うとおり必要に応じて echo コマンドでの事前確認を行うようにしましょう。さて、次回はエイリアスについて説明します。よく使うコマンドやオプションの組み合わせに分かりやすいエイリアスを設定して、より使いやすいターミナル環境を整備してみましょう。お楽しみに!

原稿: 株式会社ビズリーチ ターミナル部部長 タナカトモフミ
株式会社ビズリーチ所属。ScalaコードをVimで書く日々をおくる。たまにうっかりIntellij IDEAに浮気してしまう。社内のVim部とEmacs部を和解させ、ターミナル部に統合することに成功したが、社内はEclipse, Intellij IDEA, Sublime Text, Atomが主流のため、戦いは続く。
https://twitter.com/tanacasino

この記事はどうでしたか?

おすすめの記事

キャリアを考える

BACK TO TOP ∧

FOLLOW