主夫ときどきプログラマ

データベース、Webエンジニアリング、コミュニティ、etc

Bash初心者から初級者へのステップアップするためのTips10選

CLIでいろいろとコマンドは使っているんだけど、bashスクリプトが書けるかというと・・・。 という人がbashの文法や機能を知って初級者へとレベルアップするためのTipsを紹介します。

1. ${変数}

シェルで変数を保持することができます。変数には文字列や数値を代入することができ、値を参照する場合は変数名の前に$を付けます。変数値は $VAL でも ${VAL} でも同じように参照出来ますが ${} の方が可読性も上がるのでおすすめです。

$ A=10
$ echo ${A}
10

変数に代入する時 = の前後にスペースを入れてはいけません。変数がコマンドとして解釈されてしまいます。

$ B = 10
bash: B: command not found

bashのバージョン4では ${VAL^} で単語の先頭を大文字に、 ${VAL^^} で単語全体を大文字に変換してくれます。

$ A='hello world'
$ echo ${A}
hello world
$ echo ${A^}
Hello world
$ echo ${A^^}
HELLO WORLD

変数の値は基本的にテキストとして扱われますので一般的なプログラミング言語のように四則演算はできません。

$ A=10
$ B=15
$ echo ${A} + ${B}
10 + 15

この様な場合は後述する算術式展開 $(( 計算式 )) を使います。

2. while read n; do [command] $n; done

パイプから受け取った標準入力を1つずつ変数に保持して引数として別のコマンドに渡したい。そんな時によく使う方法が while read ... のイディオムです。 X.txt Y.txt をそれぞれ X.txt.bak Y.txt.bak との差分をみたい場合、ワンライナー(1行スクリプト)で書くとこのようになります。

$ ls *.txt | while read f ; do diff ${f} ${f}.bak; done

# 通常の書き方
$ ls *.txt |
while read f
do
  diff ${f} ${f}.bak
done

ls の出力を1行ずつ読み込み変数 f に保持し diff コマンドの引数として与えています。 羊を100匹数える場合はこのようになります。

$ seq 100 | while read n; do sleep 1 ; echo "羊が${n}"; done

3. $(( 計算式 )) 算術式展開

$(( 計算式 )) は算術式展開と呼ばれるbashの機能でいろいろな計算ができます。四則演算はもちろんのこと論理積論理和などのビット演算もできます。

# 四則演算
$ A=10
$ B=15
$ echo $(( ${A} + ${B} ))
25
$ echo $(( ${A} * ${B} ))
150

# べき乗
$ echo $(( ${A} ** ${B} ))
1000000000000000
$ echo $(( 2 ** 16 ))
65536

# modulo
$ M=$(( ${A} % ${B} ))
$ echo ${M}
10
$ echo $(( 13 % 8 ))
5

# 論理積
$ echo $(( 15 & 4 )) # 1111 と 0100 の論理積
4

# 論理和
$ echo $(( 10 | 5 )) # 1010 と 0101 の論理和
15

# 排他的論理和
$ echo $(( 13 ^ 11 )) # 1101 と 1011 の排他的論理和
6

4. || && 終了ステータス判定

コマンドはその動作が成功すると「終了ステータス」として0を返します。失敗すると0以外の数字が返ります。直前のコマンドの終了のステータスは $? 変数に保持されます。

# wget コマンドの例
$ wget 'http://google.co.jp' 2> /dev/null
$ echo ${?}
0
$ wget 'http://foo.bar.baz.co.jp' 2> /dev/null
$ echo ${?}
4

0は成功のステータス、 4は失敗のステータスで Network failure. の意味です。 2> /dev/null標準エラー出力を捨てて表示されないようにしています。

&&|| はこの終了ステータスを判定するbash演算子です。&& で成功した時の処理、|| で失敗した時の処理を書きます。

$ wget 'http://google.co.jp' 2> /dev/null && echo 'success!' || echo 'failure...'
success!
$ wget 'http://foo.bar.baz.co.jp' 2> /dev/null && echo 'success!' || echo 'failure...'
failure...

gerp は何かがパターンマッチすれば0を、何もマッチしなければ1を返します。そこで || 演算子を使うと処理を分岐することができます。

$ ls -aF | grep 'git' || echo 'no git file or dir'
no git file or dir


$  ls -aF | grep 'git' || echo 'no git file or dir'
.git/
.gitignore

5. -- オプションの打ち止め

-Rf~ というようなディレクトリが何かの拍子にできてしまいまった場合どうすればよいでしょうか。

$ ls -F
-Rf -f/ ~/

まちがっても $ rm -Rf ~ なんてコマンドを打ってはいけません。 -Rf はオプションとして解釈されてしまいます。このような場合は -- を使います。-- は特別な引数でそれ以降のオプションが打ち止めとなります。

$ rm -- -Rf
$ rmdif -- -f

~ は通常ホームディレクトリと解釈さるので、 -- を利用しても意味がありません。シングルクォートでくくる、\ でエスケープする、./ でカレントディレクトリを明示するなどが必要です。

$ rm '~'
$ rm \~
$ rm ./~

6. ヒアドキュメント

複数行のテキストをプロセスの入力にします。 << 終了文字列 を記述し複数行の文字列を入力後に 終了文字列 を記載すると入力終了の合図となります。

$ cat -n << EOF
I have a pen.
I have a apple.
EOF
     1 I have a pen.
     2 I have a apple.

標準入力に流し込むので echo ではなく cat で出力しています。この出力をリダイレクトやパイプに渡す場合は、1行目に書く必要があります。

# 出力をリダイレクト
$ cat << EOF > output.txt
I have a pen.
I have a apple.
EOF

$ cat output.txt 
I have a pen.
I have a apple.
# パイプで渡す
$ cat << EOF | jq
[{
"name":"Matz","age":51,"from":"Japan"},
{"name":"Dhh","age":36,"from":"Denmark"}
]
EOF
[
  {
    "name": "Matz",
    "age": 29,
    "from": "Japan"
  },
  {
    "name": "Dhh",
    "age": 36,
    "from": "Denmark"
  }
]

標準入力に流し込むので while イディオムの入力としても利用できます。

$ while read n; do echo $(( ${n} * 2 )); done << EOF
1
2
3
4
5
EOF
2
4
6
8
10

7. <<< ヒアストリング

<<< の右側に書いた文字列や変数の値をコマンドの標準入力に流し込みます。 ヒアドキュメントと同じように扱えます。パイプとリダイレクトも通常通り利用できます。

$ cat <<< 'Hello World!!'
Hello World!!

$ JSON='[{
"name":"Matz","age":51,"from":"Japan"},
{"name":"Dhh","age":36,"from":"Denmark"}
]'

# パイプ |
$ jq . <<< ${JSON}} | grep -i 'matz'
    "name": "Matz",

# リダイレクト >
$ jq .[].name <<< ${JSON} > name.txt
$ cat name.txt
"Matz"
"Dhh"

8. $( コマンド ) コマンド置換

コマンドの実行結果を文字列として利用するための機能です。 コマンドの引数として展開したり引数に保持したりします。

# dateコマンドの結果が文字列に展開される
$ JSON="{\"time\":\"$(date +%s)\"}"
$ echo $JSON
{"time":"1489992386"}

# yamlファイルだけを抽出して処理
$ for f in $( find . -type f | grep '\.yml$' )
do
    [command] ${f} # 何らかの処理
done

9. $(< ファイル )コマンド置換

コマンド置換の拡張的な機能でファイルの中身に置き換えられます。

# cpコマンドを使わずbashの機能だけでファイルをコピー
$ echo "$(< /tmp/foo )" > /tmp/foo.bak

コマンドの処理結果を一時ファイルに保持することで可読性が向上します。

$ find . -type f | grep '\.yml$' > /tmp/yaml.list.txt
$ for f in $(< /tmp/yaml.list.txt )
do
    [command] ${f} # 何らかの処理
done

10. <( コマンド ) プロセス置換

コマンドの入出力をファイルとして扱う機能です。引数にファイルを受け取るコマンドの入力として利用できます。

# cat は引数としてファイルを受け取る
$ cat -n <( echo 'Hello World' ) <( echo 'Dreams come true' )
     1 Hello World
     2 Dreams come true
# ファイルリストの差分を diff で得る
$ ls
a.txt      c.txt      e.txt      h.txt       j.txt      l.txt      o.txt      q.txt
a.txt.bak  c.txt.bak  f.txt      i.txt       j.txt.bak  m.txt      o.txt.bak  q.txt.bak
b.txt      d.txt      g.txt      i.txt.bak   k.txt      n.txt      p.txt      r.txt
b.txt.bak  d.txt.bak  g.txt.bak  index.html  k.txt.bak  n.txt.bak  p.txt.bak  r.txt.bak

$ diff <( ls | grep '.txt$' | cut -d. -f1 ) <( ls | grep '.txt.bak$' | cut -d. -f1 )
5,6d4
< e
< f
8d5
< h
12,13d8
< l
< m

シェルプログラミング実用テクニック 入門bash 第3版 ソフトウェアデザイン 2016年 06 月号 ソフトウェアデザイン 2017年 01 月号

2017年の目標

年始からいろいろと考えていたことをまとめるためにも今年の目標を書き出します。

セルフブランディング

Rubykaigi2016に参加したことで改めて個人として活躍するには必要なことだと感じた。 まずは情熱プログラマーにもあるように「Googleで検索し最初の4ページのリンクだけを見てどんな人物だと想像するだろうか」という部分に注視して見ようと思う。 情熱プログラマーは改めて読むとほんとに良い本だ。

コミュニティでの登壇

勉強会やカンファレンスで登壇する。 3年後にはRubykaigiに登壇することを目指す。 オリンピックイヤーか・・・。

ブログ

Qiitaも含めてアウトプットを増やす。 最低でも月1回は書く。

OSSにコミット

GitHubでPullRequestを送る。 今年こそ!!

シェルプログラミング

シェル芸から入って bash が面白くなってきたのでシェルプログラミングでいろいろ出来るようになる。

Elixir

途中で止まってしまったプログラミングElixirをちゃんと読む。 演習問題もちゃんとやって手を動かす。

運用・監視まわり

Mackerelで独自のメトリクスを取りたい、という課題があるのでちゃんと勉強して達成する。 あわせてFuluentdとか使ってログ解析も出来るような基盤を考える。

新春bash書き初めをやった

SoftwareDesignの2017/1号の第1特集である新春bash書き初め シェル30本ノックをやった。
結果は 19/30 でまぁまぁの出来だと思う。
コマンドやbashについていろいろと新しい発見があったのでまとめておく。

各問題の自分なりの回答は GitHub においてある。
雑誌はこちら

コマンドについて

sed awk

抽出や変換など知れば知るほどなんでもできそうなコマンド。

  • sed -n '/開始パターン/,/終了パターン/p'
  • sed 'p' | sed '1d;$d' | paste
  • awk '{print}{fflush()}'

while read n; do [command] $n; done

繰り返しのイディオムで頻出。xargs とうまく使い分けよう。

openssl

暗号関連のいろんなことができるコマンド。使うことはほとんどなさそう。

crontab

コマンドというか @reboot コマンド起動時に1度、指定のコマンドを実行するという設定ができることを知った。

grep

grep -q . とすると、マッチの有無で終了ステータスが変わるので処理を分岐できる。
$ (何らかの処理) | grep -q . && (出力がある場合の処理) || (出力がない場合の処理) と出来る。
-q は標準出力に何も出さないオプション。

paste

それぞれのファイルの行を並列に結合する。

$ paste file1 file2
file1の1行目 file2の1行目
file1の2行目 file2の2行目
...

標準入力も扱える。

$ seq 1 10 | paste - -
1   2
3   4
5   6
7   8
9   10

file

ファイルの種類を推定してくれるコマンド。

tee

標準出力に出力しつつ、内容をファイルに保存する。
リダイレクトでは特権(sudo)が必要なファイルは書き込めないためteeを使うとうまくいく。
$ echo 'foo bar' | sudo tee special_file.txt

rev

反転してくれる。

$ seq 1 10 | xargs
1 2 3 4 5 6 7 8 9 10
$ seq 1 10 | xargs | rev
01 9 8 7 6 5 4 3 2 1

[

test コマンドの別名。if [ xxxx ]; でよく使われるのでオプションを覚えたほうが良い。

set env

set は現在のシェル変数、 env環境変数のみを出力する。

bashについて

${変数}

変数へ代入する時はスペースを入れてはいけない。
変数を使う時は$をつける。

$ n='hello world'
$ echo ${n}
hello world

|| && 終了ステータス判定

直前に実行したコマンド(処理)の終了ステータス($0)を判定するbash演算子
$ コマンド && コマンドが成功した時の処理 || コマンドが失敗した時の処理

<<< 標準入力へ流し込む

<<< の右側に書いた文字列や変数の値を標準入力に流し込む。

$ cat <<< 'hello world'
hello world
$ cat < 'hello world'
bash: no such file or directory: hello world

$(( 計算式 )) 算術式展開

いろいろな計算ができる。四則演算はもちろんのこと、論理和論理積、ビット演算も可能。

$ echo $(( 2 * 3 + 4 ))
10

$( コマンド ) コマンド置換

コマンドの実行結果を文字列として扱える。

$ for n in $( seq 1 5 ); do echo "羊が$n匹"; done
羊が1匹
羊が2匹
羊が3匹
羊が4匹
羊が5匹

:- 変数展開

難しいのでこちら
デフォルト値への置換ができる。

--

オプションの打ち止め。 $ mkdir -- -RF-RFというディレクトリが作成される。--以降はオプションと認識されなくなる。

$(<ファイル) コマンド置換2

コマンド置換の拡張的な機能で「ファイル」の中身に置き換えられる。

$ echo "$(<ファイル)" > foo$ cp ファイル foo と同じ。

<( コマンド ) プロセス置換

コマンドの入出力をファイルとして扱うことが出来る。

$ cat < <(echo 'hello world')
hello world

さいごに

各コマンドやbashの詳しい使い方や実例は雑誌を読んでくれ。

SoftwareDesign2017.01

2016年をふりかえる

Facebookにポストしてた2016年の目標をブログに移したのでそれをベースに2016年を振り返ります。

masayuki14.hatenablog.com

ひとつひとつ見ていきましょう。さほど意識しないで1年を過ごしてきたわけですが・・・。

2016年にたてた目標

Swift2

swift2を試す。iOsネイティブのアプリを作れるようになろう。年内にクソゲーがリリースできたらバンザイ\(^o^)/

2016年の目標 - masayuki14’s diary

宣言することは大事なもので、2月に参加したスプーキーズ沖縄合宿iOSアプリチームとなりSwiftにチャレンジすることとなりました。
夏頃からアプリ開発の案件にも参加することとなり年間通じて触れる機会に恵まれました。 Swiftの言語仕様はほぼ把握したのでコーディング自体には問題ありませんが UIKit 周りが全然わかってなくて、一人でアプリ開発することはまだまだできない感じです。

Redux

Reactで作ったアプリにReduxを導入する。

2016年の目標 - masayuki14’s diary

React 0.13 で開発したのでバージョンアップにも結構コストかかりそうだったし、サービス自体もあまり変更なく稼働していたのでそのまま。
Redux導入までには至りませんでした。今後も予定はありません。そして1年通じてほとんど React に触れませんでした。

Hubot

なんかつくる。くだらないものからやっていこう。みさわの画像出すとか。すでにありそうだけど。

2016年の目標 - masayuki14’s diary

忘れてください。ごめんなさい。

Meckerel

サーバーにMackerel入れてみる。APIコールして独自のメトリクスとりたい。

2016年の目標 - masayuki14’s diary

ごめんなさい。忘れてください。

ブログ

月に1回はブログを書く。ちゃんとアウトプットしよう。

2016年の目標 - masayuki14’s diary

月1とは言わずとも少しは書きました。せっかくなのでリンクを貼っておこう。

Rubyでメタプログラミング - 主夫ときどきプログラマ

スプーキーズの勉強会イベント一般公開します。 - スプーキーズの中の人。

おじさんの嫉妬は犬も喰わない - スプーキーズの中の人。

RubyKaigi2016 に行ってきました。 - 主夫ときどきプログラマ

暖かい季節にはモチベーションも上がるようです。

IPA試験

IPAの試験を受けてなんか合格したい。DBとネットワークあたりか。春の試験は受付中。

2016年の目標 - masayuki14’s diary

データベーススペシャリスト合格しました!おめでとう!
ネットワークスペシャリストは不合格でした!次回がんばろう!

OSSコミッタ

になろう。規模の大小は問わずGitHubでPullRequestを送ってみる。

2016年の目標 - masayuki14’s diary

https://github.com/ash1day/Copy-link-as-markdown/pull/3

知人のリポジトリですがPullRequest送ったのでこれでいいんだ!はじめの一歩はこんなもんだ!

コミュニティ活動

勉強会とか参加してみよう。

2016年の目標 - masayuki14’s diary

ブログにも書きましたがRubyKaigi2016にHelper Staff として参加しました。
今年1番の大きな出来事だと思っています。今後も京都や関西のコミュニティで活動を継続していきます。

総括

本当にやりたいと思っていることは目標にしてもしなくてもやるし、 そんなに思っても見ないことを目標にしてもあんまり達成しないのかな、という印象です。

とはいえそれほど思っても見ないことを達成するにはより目標とスケジュールを一緒に設定すればいいのかな、とも思います。 2017年もしっかり目標立てて1年後ふりかえれるように頑張ろう。

RubyKaigi2016 に行ってきました。

RubyKaigi2016が京都で開催されました。

RubyKaigiが地元で開催されるのなら行くっきゃない、ということで申し込みました。
早い段階で申し込みをしたこともあり、HelperStaff募集にも応募することができ、 HelperStaffでの初参加と相成りました。

今年はHelperの応募が少なかったとのことなので、スタッフになるとこんなにいいことがあるよ、 ということを紹介したいと思います。
再び関西で開催される時には応募が増えるといいなと思います。

開催中の様子はこちらのブログにすばらしい写真がたくさんあったので御覧ください。 hazi.jp

参加することのメリット

Rubyistの仲間が増える

会場にはほぼRuby関係者が集まりますので、自然とRubyistの仲間が増えます。 熟練のRubyistも数多くいますので、見習うべき先輩達にも出会えます。

モチベーションが上がる

Rubyはもちろんのこと、Rubyで先端技術をどう扱うかといったようなセッションが数多いので自然とモチベーションがあがります。 仕事や技術に対する意識も変わってくることでしょう。

お弁当とTシャツがついてくる

参加費は早割でも10000円しますが、3日間の昼食弁当とTシャツ(数量限定)がついてくることを考えると安いと思います。 たくさんのステッカーもゲットできるのでラップトップに貼っていい感じにしましょう。

Helper Staff のメリット

スタッフTシャツもらえる

スタッフは紺色のTシャツでした。 もちろんお弁当もおなじ。

Ruby界隈のことがわかってくる

OrganizerやStaffにはRubyコミッターの方々がいます。(今回初めて知った。)
もちろん著名なコミッターの方の基調講演やセッションも行われるのでそういった人達を知ることができます。
オフィシャルパーティーや打ち上げで彼らと話す機会もありますし、 Twitterなどをフォローすればさらにいろいろな情報を得られるでしょう。

知らない人に話しかけやすい

オフィシャルパーティーやブースでもスタッフだと話しかけやすいです。
特にパーティーで知らない人と話をするときに
「スタッフなのでぜんぜんセッション聞けてないんで、今日のおすすめあれば教えて下さい。」
というのが常套手段のようで、私もパーティーではこれで話を始めることが多かったです。

変化する運営方法が間近で見られる

発生する課題にその都度対応し、翌日にはより良くなるように皆さん動いていて 変化する運営の中にいられるのはとても刺激的でおもしろい体験でした。
同時通訳レシーバーの貸出を担当しましたが、いい感じで運営できたと思います。
やはりオフラインコミュニケーションの方が改善のスピードは圧倒的に速いかもしれせん。

オフィシャルパーティーに参加できる

定員のあるオフィシャルパーティーにスタッフ枠で参加できますし、 もちろんスタッフの打ち上げにも参加できます。
今回は京都ということもあり最終日に打ち上げを行ったようです。
セミオフィシャルな形でスタッフ以外の参加もありましたが、 東京では後日にスタッフだけで打ち上げするみたいです。

ステージに上がれる

クロージングで壇上にあがりました。
会場とても広かったしこんなところでセッションできる人って やっぱりすごいんだなーと思います。

Helper Staff のデメリット

HelperStaffになるのは基本的にいいことずくめなんですが、 やはり少しだけデメリットもあります。
でも総合的にみたらメリットしかないです。

セッションが聞けない

担当にもよりますが、セッションはあまり聞けません。
特に受付担当になると会場からも遠いのでかなり難しい状況になります。
逆に会場担当だとけっこう聞けると思います。

朝が早い

開場前に集合するので当然朝が早いです。毎日8時頃にはみなさん集まっていました。

立ちっぱなし

立ちっぱなしでいる時間が長いので疲れます。筋トレをしておきましょう。

仕事がたまる

やはり平日開催なので仕事ができません。とうぜん仕事が溜まります。
空いた時間に仕事している方もいました。

交通費・宿泊費がかかる

東京から来ている方は当然それなりの費用がかかります。
このあたりをバックアップいただける企業さんは本当にありがたいと思います。
だいたいスタッフさんはこういった企業で活躍されている方のようでした。

変なテンションになった

仕事したくない
Rubyコード書きたい
のんびりしたい
でもお金欲しい

欲望に素直になりました。
2週間たっても変わらない。

おわりに

公式サイトで各セッションの動画が配信されています。
すばらしいセッションの数々を御覧ください!

おすすめはJustinさんの基調講演で、リファクタリングがしたくなります。
絵文字の使い方がとてもじょうず。

www.youtube.com

RubyKaigi はOrganizerの皆さん、多くのスポンサー企業、そしてこれまでの実績を積み上げてきた 過去に尽力頂いたたくさんの方のおかげで今年も開催できているのだなと感じました。
すべての皆さんに感謝したいと思います。ありがとうございました。

f:id:masayuki14:20160910174716j:plain

また京都で開催されますよーに。