ysapでbash入門①
bash学習記録:基礎編(Chapter 02, Section 02まで)
学習教材
- コース: ysap bash course
- YouTube: Bash Tutorial
- 進捗: Chapter 02, Section 02まで完了
学習環境の準備
zsh vs bash問題
世間はバレンタインデーの中、講師であるロン毛で髭のDave氏を画面越しに見つめてbash入門をスタート。 講師はiTerm2でbashを使用していたが、自分の環境はweztermでzsh。
結論: zshはbashの上位互換的な存在なので、そのまま進めることに決定。
- 基礎的なコマンド、スクリプト、構文はほぼ同じ
- zshの方が強力な補完機能や優れた機能が多い
- macOSのデフォルトシェルもzsh(2019年以降)
特定の演習でbash固有の挙動が必要になったら、その時だけbashコマンドで一時的に切り替える方針。
安全なファイル操作のためのalias設定
重要な発見
Ubuntuサーバーでは設定していたalias rm='rm -i'をMacの.zshrcに設定していなかったことに気づいた。
推奨設定
alias rm='rm -i' # 削除前に確認
alias cp='cp -i' # コピー時も確認
alias mv='mv -i' # 移動/リネーム時も確認
-iオプションの制限
重要: rm -rfと打つと、-f (force) フラグが -i を上書きしてしまう!
より安全な代替案
alias rm='rm -I' # -I (大文字のアイ) を使う
-Iオプションの特徴:
- 3ファイル以上削除する時、または再帰的削除(-r)の時に1回だけ確認
-fと一緒に使っても確認してくれる
最終決定: バランスの良いalias rm='rm -I'を採用。
weztermの検索機能設定
問題
iTerm2ではCtrl+Fで検索ができるが、weztermでは動作しなかった。
原因
weztermの設定でconfig.disable_default_key_bindings = trueが有効になっており、デフォルトのキーバインドが無効化されていた。
解決方法
keybinds.luaに以下を追加:
-- 検索機能
{ key = "f", mods = "CTRL", action = act.Search("CurrentSelectionOrEmptyString") },
コピーモードとの関係
コピーモード内ではCtrl+Fは「ページダウン」として動作するが、通常モードでは「検索」として動作する。同じキーでもモードによって動作が変わる設計。
コピーモードとは?
- ターミナルの出力履歴をキーボードだけで移動・選択・コピーできるモード
Leader + [(Ctrl+Q→[)で入入- Vimライクな操作(hjklで移動、vで選択開始、yでコピー)
- 上級者向けの機能だが、通常のマウス操作でも全く問題ない
コマンドライン操作の効率化
基本的な移動コマンド(Emacs由来)
Ctrl + A # 行の先頭へ
Ctrl + E # 行の末尾へ
Ctrl + W # カーソル位置の前の単語を削除
HHKBでの実用的なアプローチ
単語ジャンプ(Option + ←/→)はHHKBではOptionキーが押しにくいため、上記3つのコマンドだけで運用することに決定。
理由: この3つだけで9割のケースはカバーできる。あとはカーソルキーで微調整すれば十分。
シェルビルトインコマンドの理解
manコマンドで表示されないコマンドがある
historyコマンドなどはmanで表示されない。これは「シェルビルトイン」という特殊なコマンドだから。
コマンドの2つのタイプ
1. 外部コマンド(独立したプログラム)
ls,grep,catなど/bin/や/usr/bin/に実行ファイルが存在man lsでマニュアルが見られる
2. シェルビルトイン(シェルに組み込まれた機能)
cd,history,echo,aliasなど- シェル自体の機能なので独立した実行ファイルがない
manでは見られない(マニュアルページがない)
確認方法
type history # → history is a shell builtin
type ls # → ls is /bin/ls
ビルトインのヘルプを見る方法
help history # bashの場合
man zshbuiltins # zshの場合は全ビルトインのマニュアル
なぜビルトインが必要か?
シェルの内部状態にアクセスする必要があるから。
例:
cd→ シェルのカレントディレクトリを変更history→ シェルが記憶しているコマンド履歴を表示export→ シェルの環境変数を設定alias→ シェルのエイリアステーブルを変更
外部プログラムは別プロセスで動くので、親プロセス(シェル)の状態は変えられない。だからcdなどはビルトインである必要がある。
プログラミングの概念との類似: スコープやカプセル化と同じ。{}の中の変数を外部から直接いじれないのと似ている。
ビルトインと外部コマンドの両方が存在するケース
typeコマンドで調べると、echo、printf、killなどは両方に存在する:
type echo
# → echo is a shell builtin
# → echo is /bin/echo
なぜ両方存在するのか?
- ビルトイン版: 速い(プロセス起動不要)、シェルの機能と統合
- 外部版: 互換性のため(シェルがない環境でも使える)
どちらが実行される?
ビルトインが優先される。
明示的に外部版を使う方法
/bin/echo "Hello" # フルパス指定
command echo "Hello" # commandコマンドでビルトインをスキップ
なぜprintfがシェルにあるのか?
printfはC言語の関数だが、シェルスクリプトでも必要だから。
echoの問題点: シェルによって挙動が違う(移植性が低い)
echo "Hello\nWorld"
# bash: Hello\nWorld (そのまま表示)
# zsh: Hello
# World (改行される)
printfの利点: POSIX標準で定義されており、どのシェルでも同じ動作
printf "Hello\nWorld\n" # どのシェルでも同じ!
printf "%s: %d\n" "Count" 42 # フォーマット指定も可能
$PATHの理解
PATHとは?
コマンドの探索パス = シェルが「コマンドがどこにあるか探す場所のリスト」
動作の仕組み
echo $PATH
# /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
これは「:で区切られたディレクトリのリスト」。
lsと打つと:
- シェルが
/usr/local/bin/lsを探す → ない - 次に
/usr/bin/lsを探す → ない - 次に
/bin/lsを探す → **あった!**実行
左から順番に探して、最初に見つかったものを実行する。
「PATHを通す」の意味
export PATH="$HOME/bin:$PATH"
これで~/bin/にあるスクリプトがどこからでも実行できるようになる = 「PATHを通した」
自分の環境のPATH確認
見やすく表示する方法:
echo $PATH | tr : '\n'
主要なディレクトリの意味:
/opt/homebrew/bin- Homebrewでインストールしたコマンド/usr/local/bin- ユーザーがインストールしたコマンド/usr/bin- macOS標準のコマンド(ls, grepなど)/bin- 基本的なシステムコマンド(sh, bashなど)
PATH設定の整理
問題発見
PATHに重複したエントリがあることを発見:
echo $PATH | tr : '\n'
# .volta/binや.local/binが2回ずつ表示される
原因
.zshrcと.zprofileの両方でPATHを設定していた:
.zshrc:
export PATH="$HOME/.volta/bin:$PATH"
export PATH="$PATH:$HOME/.local/bin"
.zprofile:
export PATH="$VOLTA_HOME/bin:$PATH"
export PATH="$PATH:$HOME/.local/bin"
解決方法
.zprofileの重複したPATH設定を削除。
理由:
.zprofileはログイン時に1回だけ読まれる.zshrcは新しいシェルを開くたびに読まれる- 通常は
.zshrcに集約する方が管理しやすい
alias設定でのエラー発見
整理中に.zshrcでエラーが発生:
/Users/username/.zshrc:126: bad assignment
原因: aliasの=の前後にスペースが入っていた
# 間違い
alias rm = 'rm -i'
# 正しい
alias rm='rm -i'
重要: aliasやexportなどの代入文では、=の前後にスペースを入れてはいけない。どっかで見て「そうかそうか」と理解してたものの、実際にミスると重要さがわかるものです。
変数展開時のダブルクォートの重要性
講師の推奨事項
echo "$PATH"のようにダブルクォートを使うことを強く推奨。
理由: スペースが省略されることがあり、バグの原因になる。
具体例で理解する
ケース1: ファイル名にスペース
filename="my file.txt"
# ダメな例
cat $filename
# → cat my file.txt と解釈される
# → エラー: catは"my"と"file.txt"という2つのファイルを探す
# 正しい例
cat "$filename"
# → cat "my file.txt" と解釈される
# → 正しく1つのファイルとして扱われる
ケース2: 空の変数
myvar=""
# ダメな例
[ $myvar = "test" ]
# → [ = "test" ] となり構文エラー!
# 正しい例
[ "$myvar" = "test" ]
# → [ "" = "test" ] となり正常動作
ケース3: 改行を含む変数
list="apple
banana
cherry"
# ダメな例
echo $list
# → apple banana cherry (改行が消える)
# 正しい例
echo "$list"
# → apple
# → banana
# → cherry (改行が保持される)
絶対ルール
変数を使う時は常に"$variable"とダブルクォートで囲む
echo "$PATH" # ✅
cat "$filename" # ✅
cd "$directory" # ✅
rm "$file" # ✅
例外(クォートなしでもOK)
- 算術式の中:
((count = $num + 1)) [[ ]]の中(bashの場合):[[ $var = test ]]
ただし、これらもクォートありで書いても問題ないので、迷ったら常にダブルクォート!
これはシェルスクリプトのバグの大半の原因となる重要事項。
次回の予定
Chapter 02, Section 03から継続予定。
学びのポイント
- zsh vs bash: zshは上位互換なので問題なし
- 安全なファイル操作:
alias rm='rm -I'を設定 - シェルビルトイン: 内部状態を操作するためシェルに組み込まれている
- $PATH: コマンド探索パスのリスト、左から順に探索
- ダブルクォート: 変数展開時は必ずダブルクォートで囲む(絶対ルール)
- 設定ファイルの整理: 重複を避け、
.zshrcに集約
特に変数のダブルクォートは、シェルスクリプトでの最重要事項として強く意識する必要がある。