0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google Colabで学ぶシェルスクリプト入門

0
Last updated at Posted at 2026-07-26

今回のテーマは、Google Colabで、シェルスクリプトの初歩の初歩を学ぶです。

※Google Colabは、気軽に触れるLinuxのサンドボックス環境としても便利です。Google Colabは数時間おきに環境がリフレッシュされるため、データ・プログラムの永続的な保管には適しません。しかし裏を返せば、「うっかり環境を壊しても簡単に初期化でき、気軽にやり直せるメリットがあります。

とくに、シェルスクリプトは、環境構築・インフラ領域で重宝される言語です。気軽に触って・試して・壊して(ときには環境すら壊しながら)学ぶアプローチに、Google Colabは非常に役に立ちます。

対象読者

この記事は、次のような人を対象にしています。

  • Google Colabを使ったことがある人
  • シェルスクリプトを書いたことがない、もしくはほとんど書いたことがない人
  • CIスクリプトなどで見かける.shファイルの中身を、自力で読めるようになりたい人
  • 学んで終わりでなく、コードを「触りながら」「実行しながら」学びたい人

すぐに使えるチートシートはこちら Google Colab版

すぐに実行して、試せるコードレシピはこちら。

学習の進め方

マジックコマンド(%%writefile)で、シェルスクリプトのファイルを作成して使用してください。

%%writefile s01_hello.sh

作成したファイルは、次のコードで実行できます。

!sh s01_hello.sh

同じファイル名を使い回すと、前の内容で上書きされてしまいます。 そのため本記事では、セルごとにs01_, s02_...と連番のファイル名を分けています。ランタイムがリセットされるまでは、全てのファイルがColab上に残るので、後から見返したり、書き換えて再実行したりして試してみてください。

1. まずは基本形 ── シェルスクリプトはシェバン行から始まる

いちばん小さなシェルスクリプトです。

#!/bin/sh
echo "Hello, Shell!"

実行は、次のように行います。

!sh s01_hello.sh
Hello, Shell!

それぞれの意味は、ざっくり次のとおりです。

  • #!/bin/sh
    このスクリプトを、/bin/shというインタプリタで実行してほしい、という宣言です。ファイルの1行目に書く決まりごとで、「シェバン行」と呼ばれます。

  • echo
    文字を画面に表示するコマンドです。

shは、機能を絞った最小限のシェルです。bashというより高機能なシェルもありますが、CIスクリプトなどでは、環境を選ばず動くshがあえて選ばれることがよくあります。

2. 変数を使う

シェルスクリプトでも、変数に値を入れることができます。

#!/bin/sh
name="Alice"
echo "$name"
Alice

name="Alice"は、nameという変数に"Alice"という文字列を入れています。変数を使うときは$nameのように$を付けます。

注意点: =の前後にスペースを入れてはいけない

シェルスクリプトには、変数の代入について1つ厳格なルールがあります。

#!/bin/sh
name = "Alice"
echo "$name"
s02b_vars_bad.sh: 2: name: not found

name = "Alice"のように、=の前後にスペースを入れると壊れます。シェルはnameというコマンドを実行しようとして、「そんなコマンドは見つからない」というエラーになります。シェルスクリプトでは、=の前後には絶対にスペースを入れない、と覚えておく必要があります。

3. コマンドライン引数を受け取る

スクリプトの実行時に渡した引数は、$1, $2...のように受け取れます。

#!/bin/sh
echo "1つ目の引数: $1"
echo "引数の数: $#"

引数を渡して実行してみます。

!sh s03_args.sh Alice Bob
1つ目の引数: Alice
引数の数: 2
  • $1は1つ目の引数、$2は2つ目の引数
  • $#は、渡された引数の数
  • 全部の引数をまとめて使いたい場合は$@も使えます

このスクリプトはAliceBobという2つの引数を渡して実行したので、$1Alice$#2になっています。

4. ユーザーからの入力を受け取る read

readを使うと、キーボードからの入力を変数に受け取れます。

#!/bin/sh
echo "名前を入力してください:"
read name
echo "こんにちは、$name さん"

Colabのセルでもそのまま実行でき、実行中に入力欄が表示されます。

!sh s04_read.sh
名前を入力してください:
inclusive solutions
こんにちは、inclusive solutions さん

実際に手元のターミナルで実行する場合も、sh s04_read.shとすれば、その場でキーボード入力を求められます。

5. 変数はダブルクォートで囲む

$nameをそのまま使うか、"$name"のようにダブルクォートで囲むかで、挙動が変わることがあります。

#!/bin/sh
name="Alice   Bob"

echo "クォートなし(連続スペースが消える):"
echo $name

echo "クォートあり(そのまま保たれる):"
echo "$name"
クォートなし(連続スペースが消える):
Alice Bob
クォートあり(そのまま保たれる):
Alice   Bob

クォートなしの$nameは、いったん単語に分割されてから並べ直されるため、連続するスペースが1つに詰まってしまいます(この現象を"word splitting"と呼びます)。クォートありの"$name"は、変数の中身をそのまま1つの文字列として扱います。

この違いは、見た目の問題だけでは済みません。次の[ ]と組み合わせると、実際にエラーになるケースがあります。

#!/bin/sh
name="Alice Bob"

echo "クォートなしで [ ] に渡すと:"
if [ -n $name ]; then
  echo "空じゃない"
fi
クォートなしで [ ] に渡すと:
s05b_quote_test.sh: 5: [: Alice: unexpected operator

$nameAliceBobという2つの単語に分かれてしまい、[ -n Alice Bob ]のような、[ ]が想定していない形になってエラーになりました。変数を[ ]に渡すときは、必ずダブルクォートで囲む、というのが鉄則です。これは、目的のコードの[ -n "$unformatted" ]にも直結する重要なポイントです。

6. コマンドの実行結果を変数に入れる ── コマンド置換

$(...)を使うと、コマンドを実行して、その標準出力を文字列として変数に受け取れます。

#!/bin/sh
today=$(date +%Y-%m-%d)
echo "今日の日付: $today"
今日の日付: 2026-07-26

$(date +%Y-%m-%d)は、「date +%Y-%m-%dというコマンドを実行して、その結果を文字列として受け取る」という意味です。これが、目的のコードのunformatted=$(gofmt -l .)の正体です。**「gofmt -l .を実行して、その出力をunformattedという変数に詰め込む」**という処理だった、ということになります。

7. 四則演算をする

シェルスクリプトでは、変数は基本的に「文字列」として扱われます。

#!/bin/sh
a=3
b=4
echo "文字列として+をそのまま書くと: $a + $b"
echo "算術展開を使うと: $((a + b))"
文字列として+をそのまま書くと: 3 + 4
算術展開を使うと: 7

$a + $bとそのまま書いても、文字として3 + 4が表示されるだけで、計算はされません。計算したい場合は、$(( ))という算術展開という書き方を使う必要があります。「基本は文字列、計算したいときは専用の書き方をする」というのが、シェルスクリプトの考え方です。

8. 条件分岐 ── ifの基本形

#!/bin/sh
x=3

if [ "$x" -gt 5 ]; then
  echo "big"
elif [ "$x" -lt 5 ]; then
  echo "small"
else
  echo "equal"
fi
small

ifの基本的な形は、次のとおりです。

  • 条件式は[ ]で囲む
  • 処理のまとまりはthenfiで囲む(fiifを逆から書いたもの)
  • 「それ以外の場合」はelif(else ifを1単語にしたもの)
  • thenを同じ行に書く場合は、直前にセミコロン;が必要(改行する場合は不要)

9. [ ]の正体 ── 実はtestという名前の"コマンド"

[ ]は記号に見えますが、実はtestという名前のコマンドの別名です。

#!/bin/sh
x=""
if [ -n "$x" ]; then
  echo "空じゃない"
else
  echo "空です"
fi
空です

[が「コマンド」だからこそ、[の直後と]の直前には必ずスペースが必要です。試しに詰めて書いてみます。

#!/bin/sh
x="hello"
if [-n "$x" ]; then
  echo "空じゃない"
fi
s09b_test_bad.sh: 3: [-n: not found

[-nという、存在しない1つのコマンドとして解釈されてしまい、エラーになりました。[はコマンド名なので、引数(-nなど)との間にスペースが要る、という理屈です。

代表的なテスト演算子を整理します。

演算子 意味
-n "$x" 文字列が空でない
-z "$x" 文字列が空である
"$a" = "$b" 文字列として等しい
"$a" != "$b" 文字列として等しくない
"$a" -eq "$b" 数値として等しい
"$a" -gt "$b" 数値として「より大きい」
"$a" -lt "$b" 数値として「より小さい」

文字列比較は=/!=、数値比較は-eq/-gt/-ltと、書き方が全く違う点に注意してください。"$a" > "$b"のように書くと、数値の比較ではなく、別の意味(リダイレクト)に解釈されてしまいます。

10. ファイル・ディレクトリの存在確認

[ ]には、ファイルの状態を調べる演算子もあります。

#!/bin/sh
echo "hello" > sample.txt

if [ -f "sample.txt" ]; then
  echo "ファイルがあります"
fi

if [ -d "sample.txt" ]; then
  echo "ディレクトリです"
else
  echo "ディレクトリではありません"
fi

if [ -e "notfound.txt" ]; then
  echo "存在します"
else
  echo "存在しません"
fi
ファイルがあります
ディレクトリではありません
存在しません
  • -f: 普通のファイルかどうか
  • -d: ディレクトリかどうか
  • -e: (種類を問わず)存在するかどうか

sample.txtはファイルとして作成したので、-fは真、-dは偽になっています。

11. 終了コード(exit status)という考え方

コマンドは、実行し終わると必ず「終了コード」という数値を返します。直前のコマンドの終了コードは$?で確認できます。

#!/bin/sh
false
echo "false の直後の \$?: $?"

true
echo "true の直後の \$?: $?"

exit 3

スクリプト自体の終了コードも、続けて確認してみます。

!sh s11_exitstatus.sh; echo "スクリプト自体の終了コード: $?"
false の直後の $?: 1
true の直後の $?: 0
スクリプト自体の終了コード: 3
  • 0なら成功
  • 0以外なら失敗

というのが、Unix共通のルールです。falseは「必ず失敗する」、trueは「必ず成功する」だけのコマンドなので、それぞれ10が返っています。exit 3のように、スクリプト自身の終了コードを明示的に指定することもできます。

以前のgo vet入門で見た「go vetが非ゼロの終了コードを返す」という話も、この仕組みの上に成り立っています。

12. コマンドをつなげる &&||

ifを使わずに、「前のコマンドが成功したら」「失敗したら」で処理をつなげる、簡易な書き方もあります。

#!/bin/sh
mkdir mydir && cd mydir && echo "ディレクトリ作成に成功しました"
cd ..
rmdir mydir

gofmt -l notfound.go 2>/dev/null || echo "gofmtの実行に失敗しました(存在しないファイル)"
ディレクトリ作成に成功しました
gofmtの実行に失敗しました(存在しないファイル)
  • A && B: Aが成功したら、Bも実行する
  • A || B: Aが失敗したら、Bを実行する

mkdir mydir && cd mydir && echo ...は、ディレクトリ作成・移動が両方成功して初めてechoまで到達します。gofmt -l notfound.go || echo ...は、存在しないファイルを指定してgofmtが失敗したので、||の後ろが実行されました。

13. for文で繰り返す

まず、対象となるファイルを何個か用意します。

!touch a.go b.go c.txt
#!/bin/sh
for f in *.go; do
  echo "$f"
done
a.go
b.go

*.goは、「.goで終わるファイル名すべて」に展開される、シェル特有の記法です(グロブと呼ばれます)。c.txtは対象にならないので表示されません。

14. while文で繰り返す

#!/bin/sh
i=1
while [ "$i" -le 3 ]; do
  echo "$i"
  i=$((i + 1))
done
1
2
3

[ "$i" -le 3 ]が真である間、繰り返します。i=$((i + 1))のように、算術展開(検証7)を使って1ずつ増やしています。

15. 関数を作る

#!/bin/sh
greet() {
  echo "Hello, $1"
}
greet "Alice"
Hello, Alice

シェルスクリプトの関数は、greet() { ... }のように定義します。引数は、関数の中でも$1, $2...で受け取ります(検証3のスクリプト全体への引数と同じ考え方です)。名前を付けて引数を受け取る仕組みはなく、あくまで位置だけで受け取る点が特徴です。

16. パイプでコマンドをつなぐ

あるコマンドの出力を、そのまま次のコマンドの入力として渡すのがパイプ(|)です。

!ls *.go | wc -l
2

ls *.go(.goファイルの一覧)の出力を、wc -l(行数を数えるコマンド)にそのまま渡しています。検証6の「コマンド置換($(...)、結果を変数に詰める)」と、この「パイプ(結果を次のコマンドに渡す)」は似ていますが、変数に残したいか、そのまま次のコマンドに流したいかで使い分けます。検証4のreadのところで使った入力の流し込みも、実はこのパイプの応用です。

17. 標準出力とエラー出力を分ける・リダイレクト

#!/bin/sh
echo "ログ1行目" > log.txt
echo "ログ2行目" >> log.txt
echo "エラーです" 1>&2

echo "log.txtの中身:"
cat log.txt
エラーです
log.txtの中身:
ログ1行目
ログ2行目
  • >: 出力をファイルに書き込む(上書き)
  • >>: 出力をファイルに追記する
  • 1>&2: 標準出力(1)を標準エラー出力(2)に向ける

echo "エラーです" 1>&2の部分だけ、通常の出力(stdout)とは別の経路(stderr)に流れるため、出力される順番が前後することがあります(今回の実行結果でも、コード上は後半にあるecho "エラーです"が一番上に表示されています)。検証12で使ったgofmt -l notfound.go 2>/dev/null2>/dev/nullも、この仲間で、「標準エラー出力を捨てる」という意味です。

18. おまけ: set -eでエラー時に即終了させる

CIスクリプトでは、set -eという書き方もよく見かけます。

#!/bin/sh
echo "1行目"
false
echo "2行目(本当は実行してほしくない)"
!sh s19_without_sete.sh; echo "終了コード: $?"
1行目
2行目(本当は実行してほしくない)
終了コード: 0

set -eを付けていないと、false(失敗)があっても、スクリプトはそのまま次の行を実行し続けます。

#!/bin/sh
set -e
echo "1行目"
false
echo "2行目(set -eがあれば、ここは実行されない)"
!sh s19_with_sete.sh; echo "終了コード: $?"
1行目
終了コード: 1

set -eを先頭に書くと、「途中で1つでも失敗したコマンドがあれば、そこでスクリプト全体を即終了する」という動きになります。

ちなみに、今回レビューした目的のコードはset -eを使わず、if [ -n "$unformatted" ]で明示的に判定してexit 1していました。gofmt -l .自体は(整形漏れがあっても)エラー終了するコマンドではないので、set -eだけに頼らず、出力の中身を見て自分で判定する、という設計になっている、と読み取れます。

弊社について

本記事を書いている 合同会社インクルーシブソリューションズ は、データ基盤構築・分析基盤設計・システム改善支援を中心に活動している小規模IT法人です。

主な領域は、

  • データマート設計・データパイプライン構築
  • SQL / Python を用いたデータ処理設計
  • BI導入支援・分析基盤の整備
  • 既存システムの運用改善・可視化支援

といった、「データを使える状態にする」ための活動です。

弊社の企業活動に興味がある方は、ぜひ公式サイトも覗いてみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?