変数と文字列
変数と代入
Bash 変数はデフォルトで型なしの文字列です。値が数値の場合は算術演算が機能します。代入で = の周りにスペースを決して置かないでください — 'name = Alice' は name というコマンドを実行しようとします。コマンド置換には $(...) を使用します(バックティックより推奨)。export は変数を子プロセスで利用可能にし、readonly は不変にします。環境変数(export されたもの)は起動したスクリプトやプログラムに継承されます。
# no spaces around = in assignments
name="Alice"
age=30
PI=3.14
active=true
# use variables with $ prefix
echo "Hello, $name!"
echo "Age: $age"
# command substitution: capture command output
today=$(date +%Y-%m-%d)
files=$(ls | wc -l)
echo "Today is $today, $files files"
# readonly and environment variables
readonly MAX=100
export PATH="$PATH:/opt/bin"
env | grep PATH文字列のパラメータ展開
パラメータ展開は Bash の最も強力な機能の1つで、${...} で囲みます。${#var} は長さを返し、${var:offset:length} は部分文字列を抽出します(負のオフセットは末尾から数えます — - の前にスペースが必要)。,, と ^^ は大文字小文字を変換します(Bash 4+)。:- は var を設定せずにデフォルトを提供し、:= は副作用として設定し、:? はエラーで中止します — スクリプトでの必須引数チェックに最適です。
s="Hello World"
echo ${#s} # length: 11
echo ${s:0:5} # substring: Hello
echo ${s:6} # from index 6: World
echo ${s: -5} # last 5 chars: World
# case conversion (Bash 4+)
echo ${s,,} # lowercase: hello world
echo ${s^^} # uppercase: HELLO WORLD
echo ${s^l} # capitalize first if 'l': Hello World
# default values
echo ${name:-Guest} # use Guest if name unset
echo ${name:=Guest} # assign Guest if unset, then use
echo ${name:?required} # error and exit if unset文字列の検索と置換
/ パターンは置換し、// はすべて置換し、# は先頭でマッチし、% は末尾でマッチします。## と %% は貪欲(最長マッチ)で、# と % は非貪欲です。これらはパス操作に不可欠です:${path##*/} は basename を抽出し(basename コマンドのように)、${path%/*} はディレクトリを抽出します(dirname のように)。これらを習得すると、sed や cut のような外部コマンドの多くの呼び出しを回避できます。
s="Hello World Hello"
# replace first occurrence
echo ${s/Hello/Hi} # Hi World Hello
# replace all occurrences
echo ${s//Hello/Hi} # Hi World Hi
# replace at beginning / end
echo ${s/#Hello/Hi} # Hi World Hello (prefix)
echo ${s/%Hello/Hi} # Hello World Hello (no suffix match)
# delete patterns (replace with empty)
echo ${s//Hello/} # World
path="/usr/local/bin/app"
echo ${path##*/} # app (longest prefix)
echo ${path%/*} # /usr/local/bin (shortest suffix)
# extract filename and extension
file="report.tar.gz"
echo ${file%.*} # report.tar (remove ext)
echo ${file##*.} # gz (keep ext)クォーティングと特殊文字
単一引用符はすべてを文字通りに保持します — 変数展開もエスケープ処理もありません(単一引用符内に単一引用符を含めることもできません)。二重引用符は $、バックティック、 の展開を許可しながらスペースと特殊文字を保持します — 変数の周りにはほぼ常に二重引用符を優先して、単語分割とグロブのバグを防ぎます。\n や \t のような ANSI-C エスケープには $'...' を使用します。
# single quotes: literal, no expansion
echo 'Hello $USER' # Hello $USER
# double quotes: expansion happens
echo "Hello $USER" # Hello alice
# escape special chars with backslash
echo "Price: \\$5.00" # Price: $5.00
echo 'It\'s a test' # It's a test (escape inside single)
# concatenate strings
a="Hello"
b="World"
c="$a, $b!"
echo $c # Hello, World!
# multi-line string
msg="Line 1
Line 2
Line 3"
echo "$msg"read とユーザー入力
read はユーザー入力を取得します。-p はプロンプトを表示し、-t はタイムアウトを設定し(非ゼロを返す)、-a は配列に読み込みます。ファイルを1行ずつ読み込む場合、常に 'IFS= read -r' を使用してください — IFS= は前後の空白を保持し、-r はバックスラッシュエスケープを無効にしてバックスラッシュを文字通りに保持します。これはファイルを1行ずつ処理する標準的な安全なパターンです。
# read a single value
echo -n "Enter your name: "
read name
echo "Hi, $name!"
# read multiple values
read -p "First and last: " first last
# read with timeout (seconds)
read -t 5 -p "Quick! " answer || echo "too slow"
# read into an array
echo "Enter colors:"
read -a colors
echo "First: ${colors[0]}"
# read line by line from a file
while IFS= read -r line; do
echo ">>> $line"
done < file.txt配列
インデックス配列
Bash 配列はゼロベースのインデックスです。@ と * はすべての要素に展開します — スペースを含む要素を正しく処理するには常に引用符で囲んでください("${arr[@]}")。${#arr[@]} は数を返します。+= は追加します。unset はインデックスにギャップを残します。再インデックスするには arr=("${arr[@]}") を使用します。${arr[@]:start:count} でスライスします。
# create and populate
fruits=("apple" "banana" "cherry")
fruits[3]="date"
# access elements
echo ${fruits[0]} # apple
echo ${fruits[@]} # all: apple banana cherry date
echo ${fruits[*]} # same as @ in most contexts
# count and slice
echo ${#fruits[@]} # count: 4
echo ${fruits[@]:1:2} # elements 1-2: banana cherry
# append
fruits+=("elderberry")
# iterate
for f in "${fruits[@]}"; do
echo "- $f"
done
# delete an element
unset fruits[1] # removes "banana" (gap remains)連想配列(マップ)
連想配列(declare -A)は他の言語の辞書のように文字列キーを値にマップします — Bash 4+ で利用可能です。${!arr[@]} でキーを、${arr[@]} で値を取得します。-v テストでキーが存在するか確認します。インデックス配列とは異なり、キーは任意の文字列なので、スペースを含むキーの引用符が不可欠です。これらは Bash 4+ が必要です(macOS はデフォルトで Bash 3 を同梱)。
# must declare before use (Bash 4+)
declare -A ages
# key-value assignment
ages[alice]=30
ages[bob]=25
ages["carol smith"]=28
# access by key
echo ${ages[alice]} # 30
echo ${ages[bob]} # 25
# all keys and values
echo ${!ages[@]} # keys: alice bob carol smith
echo ${ages[@]} # values: 30 25 28
echo ${#ages[@]} # count: 3
# iterate keys
for name in "${!ages[@]}"; do
echo "$name is ${ages[$name]}"
done
# check if key exists
if [[ -v ages[alice] ]]; then
echo "alice exists"
fi配列の反復パターン
3つの主な反復スタイルがあります:値による(for x in "${arr[@]}")、インデックスによる(for i in "${!arr[@]}")、C スタイル。インデックス形式は位置が必要な場合に便利です。スペースを含む要素が単一アイテムとして保持されるよう、常に "${arr[@]}" を引用符で囲んでください。ループ内で += で配列を構築するのが結果を蓄積する慣用的な方法です。
arr=(one two three four five)
# iterate by value
for item in "${arr[@]}"; do
echo "$item"
done
# iterate by index
for i in "${!arr[@]}"; do
echo "[$i] = ${arr[$i]}"
done
# C-style indexed loop
for ((i=0; i<${#arr[@]}; i++)); do
echo "${arr[$i]}"
done
# map/transform: build a new array
upper=()
for w in "${arr[@]}"; do
upper+=("$(echo $w | tr a-z A-Z)")
done
echo "${upper[@]}"コマンド出力からの配列
mapfile/readarray(Bash 4+)は1回で配列に行を読み込みます — 大きなファイルでは while ループよりはるかに高速です。区切り文字列を分割するには、IFS を設定して read -ra を使用します。ファイル名(スペースや改行を含む可能性あり)には、常に find -print0 を read -d '' と組み合わせて null バイトで分割します — ファイル名の唯一の安全な区切り文字です。< <(...) プロセス置換はサブシェルなしでループに供給します。
# split a string into an array
csv="a,b,c,d"
IFS=',' read -ra parts <<< "$csv"
echo ${parts[2]} # c
# read lines of a file into an array
mapfile -t lines < file.txt
echo "${lines[0]}" # first line
echo "${#lines[@]}" # line count
# alternative: readarray (same as mapfile)
readarray -t lines2 < file.txt
# from command output (words split on IFS)
files=($(ls *.txt))
echo "${files[@]}"
# safer: read null-delimited output
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find . -name "*.txt" -print0)特殊変数
これらの特殊変数はスクリプティングに不可欠です。$0 はスクリプトパス、$1-$9 は位置引数(2桁は ${10} を使用)。スペースを含む引数を保持するには常に "$@" を引用符で囲んでください — "$*" は1つの文字列に結合します。$? は終了ステータス(0 = 成功)で、すべてのエラーチェックの基礎です。$$ と $! は PID ファイルとプロセス管理に便利です。shift は $1 を破棄し残りを下にずらします。
# positional parameters
echo $0 # script name
echo $1 # first argument
echo $# # argument count
echo $@ # all arguments (separate words)
echo "$@" # all arguments (preserves quoting)
echo $* # all arguments (single string)
# process and shell info
echo $$ # current shell PID
echo $! # PID of last background command
echo $? # exit status of last command (0 = success)
echo $- # current shell option flags
echo $_ # last argument of previous command
# shift arguments
echo "$1 $2" # a b
shift
echo "$1 $2" # b c
# iterate all args safely
for arg in "$@"; do
echo "arg: $arg"
done制御フローとテスト
If / Elif / Else
[ ] は POSIX テストコマンドです(ポータブルだが制限あり)。[[ ]] は Bash の拡張版で、パターンマッチング(ワイルドカード付き ==)、正規表現(=~)、&& / || 演算子、変数の引用符が不要をサポートします。Bash スクリプトでは [[ ]] を優先してください。[ ] では空やスペースを含む値での構文エラーを避けるため、常に変数を引用符で囲んでください。-f、-r、-d、-e テストはファイルの存在と属性をチェックします。
# basic if
if [ "$age" -ge 18 ]; then
echo "adult"
elif [ "$age" -ge 13 ]; then
echo "teen"
else
echo "child"
fi
# modern [[ ]] test (Bash-specific, safer)
if [[ $name == "Alice" ]]; then
echo "hi Alice"
fi
# pattern matching in [[ ]]
if [[ $file == *.txt ]]; then
echo "text file"
fi
# regex matching
if [[ $email =~ ^[a-z]+@[a-z]+.[a-z]+$ ]]; then
echo "valid email"
fi
# multiple conditions
if [[ -f file.txt && -r file.txt ]]; then
echo "readable file"
fiテスト演算子
ファイルテスト(-f、-d、-r など)はファイルシステム属性をチェックします。文字列テストは = と !=(または [[ ]] では ==)を使用し、-z は空、-n は非空をチェックします。整数比較には -eq、-ne、-lt、-gt、-le、-ge を使用します — [ ] で整数に < > を使用しないでください(リダイレクトになります!)。[[ ]] と (( )) ではおなじみの < > <= >= 演算子を使用できます。これらの演算子を暗記することが正しい条件分岐を書くために不可欠です。
# file tests
[ -f file ] # exists and is regular file
[ -d dir ] # exists and is directory
[ -e path ] # exists (any type)
[ -r file ] # readable
[ -w file ] # writable
[ -x file ] # executable
[ -s file ] # exists and is non-empty
[ file1 -nt file2 ] # newer than
[ file1 -ot file2 ] # older than
# string tests
[ -z "$s" ] # empty string
[ -n "$s" ] # non-empty string
[ "$a" = "$b" ] # equal (use == in [[ ]])
[ "$a" != "$b" ] # not equal
# integer tests
[ $n -eq 5 ] # equal
[ $n -ne 5 ] # not equal
[ $n -lt 5 ] # less than
[ $n -gt 5 ] # greater than
[ $n -le 5 ] # less or equal
[ $n -ge 5 ] # greater or equalcase 文
case は Bash の switch に相当します — 値を glob パターンに対してマッチします。| は代替を区切ります。;; はブランチを終了します(break のように)。*) パターンがデフォルトです。パターンはワイルドカード(*、?、[abc])をサポートしますが、完全な正規表現はサポートしません。case は単一の値でのディスパッチに長い if-elif チェーンよりクリーンで、コマンドラインサブコマンド(start/stop/restart)を構築する標準的な方法です。
case $1 in
start)
echo "Starting service..."
systemctl start app
;;
stop)
echo "Stopping service..."
systemctl stop app
;;
restart)
$0 stop
$0 start
;;
status)
systemctl status app
;;
--help|-h)
echo "Usage: $0 {start|stop|restart|status}"
;;
*)
echo "Unknown command: $1" >&2
exit 1
;;
esac
# pattern matching in case
case $file in
*.jpg|*.png) echo "image" ;;
*.mp3|*.wav) echo "audio" ;;
*.txt) echo "text" ;;
*) echo "other" ;;
esac算術と (( ))
$(( )) は算術式を評価して結果を返します。(( )) 内では変数名に $ は不要 — 名前のみを使用します。コマンドとしての (( )) は結果が非ゼロの場合に終了ステータス0(真)を返し、数値条件に理想的です。Bash は整数算術のみを行います。浮動小数点には bc や awk を使用してください。演算子:+ - * / % ** と C スタイルの比較・ビット演算子がすべて動作します。
# arithmetic expansion
x=5
y=$((x + 3))
echo $y # 8
echo $((x * 2)) # 10
echo $((2 ** 10)) # 1024 (exponent)
echo $((17 % 5)) # 2 (modulo)
echo $((17 / 5)) # 3 (integer division)
# (( )) condition: no $ needed, returns exit status
x=10
if (( x > 5 && x < 20 )); then
echo "in range"
fi
# increment / decrement
((x++)) # post-increment
((x--)) # post-decrement
((++x)) # pre-increment
# bitwise operators
echo $((5 & 3)) # 1 (AND)
echo $((5 | 2)) # 7 (OR)
echo $((5 << 1)) # 10 (left shift)ショートサーキットと三項
&& と || はショートサーキット演算子で、簡潔な制御フローとしても機能します:'cmd1 && cmd2' は cmd1 が成功した場合のみ cmd2 を実行し、'cmd1 || cmd2' は cmd1 が失敗した場合のみ cmd2 を実行します。これは1行でシンプルな if-else を置き換えます。'A && B || C' パターンは三項を模倣しますが、B が失敗する可能性がある場合は微妙なバグがあります — 堅牢なコードには実際の if 文を使用してください。コロン(:)は空のブランチに便利な no-op コマンドです。
# && and || as short-circuit control flow
[[ -f file.txt ]] && echo "exists"
[[ -f file.txt ]] || echo "missing"
# combined: do this OR fail
mkdir -p build && cd build && make || exit 1
# ternary-like (Bash doesn't have a real ternary)
result=$([[ $x -gt 0 ]] && echo "positive" || echo "non-positive")
# default value via &&
echo ${name:-"anonymous"}
# run command, fallback on failure
ping -c1 host && echo "up" || echo "down"
# null command as no-op
if condition; then
: # do nothing
fiループと反復
For ループ
for ループは単語のリストを反復します。ブレース展開 {1..5} はシーケンスを生成します(オプションのステップ {start..end..step} 付き)。C スタイルの for ((init; cond; update)) は数値カウンタに最適です。*.txt でファイルを反復する場合、引用符なしの glob は安全に展開しますが、ファイルがマッチしない場合はリテラル '*.txt' になります — shopt -s nullglob を有効にして空のリストを得てください。
# iterate a list
for fruit in apple banana cherry; do
echo "$fruit"
done
# iterate an array
colors=("red" "green" "blue")
for c in "${colors[@]}"; do
echo "Color: $c"
done
# range with brace expansion
for i in {1..5}; do
echo "Number: $i"
done
# range with step
for i in {0..20..2}; do echo $i; done # 0 2 4 ... 20
# C-style for loop
for ((i=0; i<5; i++)); do
echo "Iteration $i"
done
# iterate command output
for file in *.txt; do
echo "Processing $file"
doneWhile と Until ループ
while は条件が成功する(exit 0)限り実行し、until は成功するまで実行します — これらは対です。'while read' パターンはファイルを1行ずつ処理する標準的な方法です。常に 'IFS= read -r' を使用してください。while ループにパイプするとサブシェルで実行されるため、変数の変更が持続しません — 変数を保持する必要がある場合はプロセス置換 '< <(cmd)' を 使用してください。
# while: runs while condition is true
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done
# read lines from a file
while IFS= read -r line; do
echo "Line: $line"
done < input.txt
# infinite loop with break
while true; do
read -p "Quit? (y/n) " ans
[[ $ans == "y" ]] && break
done
# until: runs until condition is true (opposite of while)
n=0
until [ $n -ge 3 ]; do
echo "n=$n"
((n++))
done
# while reading from a pipeline
seq 1 5 | while read n; do
echo "got $n"
doneBreak、Continue と select
break はループを終了し(break N は N 個のネストしたループを終了)、continue は次の反復にジャンプします。select はリストからインタラクティブな番号付きメニューを作成し — break されるまでループします。これらの制御文は for、while、until ループで動作します。break N 形式は深くネストしたループから脱出するのに不可欠ですが、コードが追いにくくなるため控えめに使用してください。
# break exits the loop
for i in {1..10}; do
[[ $i -eq 5 ]] && break
echo $i # prints 1 2 3 4
done
# continue skips to next iteration
for i in {1..6}; do
(( i % 2 == 0 )) && continue
echo $i # prints 1 3 5 (odd only)
done
# break out of nested loops
for i in 1 2 3; do
for j in a b c; do
[[ $j == "b" ]] && break 2 # break both loops
echo "$i$j"
done
done
# select menu
select opt in "Start" "Stop" "Quit"; do
case $opt in
Start) echo "starting" ;;
Stop) echo "stopping" ;;
Quit) break ;;
*) echo "invalid" ;;
esac
doneファイルの安全なループ
ls 出力の解析は古典的なバグです — スペース、改行、特殊文字を含むファイル名が壊します。for ループでシェル glob(*.txt)を直接使用し、変数を引用符で囲んでください。再帰的または複雑な検索には、find -print0 を 'read -d ''' にパイプします — null バイトがファイル名の唯一の安全な区切り文字です。マッチしない glob がリテラルパターンではなく空のリストを生成するよう nullglob を有効にしてください。
# BAD: breaks on filenames with spaces
for f in $(ls *.txt); do
cat "$f"
done
# GOOD: glob expands safely
for f in *.txt; do
[[ -f "$f" ]] || continue
cat "$f"
done
# BEST: handle spaces, newlines, and no matches
shopt -s nullglob dotglob
for f in *.txt; do
echo "Processing: $f"
done
# find with -print0 and while read -d ''
while IFS= read -r -d '' f; do
echo "Found: $f"
done < <(find . -type f -name "*.txt" -print0)
# process files modified today
for f in $(find . -mtime -1 -print); do
echo "Recent: $f"
done範囲とシーケンス生成
ブレース展開 {a..b} は解析時にシーケンスを生成します — 高速で組み込みです。文字、数字、ゼロパディング、ステップ({start..end..step})をサポートします。seq はより多くのフォーマットオプション(-f で printf スタイル)を持つ外部コマンドです。ブレース展開は複数の引数も作成します:echo file{1..3}.{txt,log} は6つのファイル名を生成します。シンプルな範囲にはブレース展開を優先し、カスタムフォーマットが必要な場合は seq を使用してください。
# brace expansion (not a loop, but generates lists)
echo {1..10} # 1 2 3 4 5 6 7 8 9 10
echo {a..e} # a b c d e
echo {01..10} # zero-padded: 01 02 ... 10
echo file{1..3}.txt # file1.txt file2.txt file3.txt
# seq command
seq 1 5 # 1 2 3 4 5
seq 1 2 10 # 1 3 5 7 9 (step 2)
seq -f "%03d" 1 5 # 001 002 003 004 005
# use seq in a for loop
for i in $(seq 1 5); do
echo $i
done
# generate a list of dates
for d in $(seq -f "2024-01-%02g" 1 31); do
echo "$d"
done関数
関数の定義と呼び出し
Bash の関数は name() または function name で定義します。スペース区切りの引数で名前で呼び出します(括弧なし)。グローバルスコープの汚染を避けるため、関数内の変数には常に 'local' を使用してください — これがないと、代入が漏れ出します。Bash 関数は直接値を返せません(return は終了ステータス0-255)。文字列を返すには echo して $() で取得します。
# two equivalent syntaxes
greet() {
echo "Hello, $1!"
}
function greet2() {
echo "Hi, $1!"
}
# call with arguments (no parens)
greet "Alice" # Hello, Alice!
greet2 "Bob" # Hi, Bob!
# local variables (function-scoped)
counter() {
local count=0
((count++))
echo $count
}
# return a value via echo + command substitution
add() {
echo $(($1 + $2))
}
result=$(add 3 4) # 7引数とパラメータ
関数内では $1、$@、$# はスクリプトの引数ではなく関数の引数を参照します — スクリプトレベルのパラメータをシャドウします。$FUNCNAME は関数名を保持します(デバッグに便利)。関数内の shift は関数のパラメータにのみ影響します。スクリプトの引数を関数に渡すには func "$@" を使用します。このシャドウイングが関数が再利用可能なビルディングブロックである理由です。
# all positional params work inside functions
show_args() {
echo "Function name: $FUNCNAME"
echo "First arg: $1"
echo "Second arg: $2"
echo "All args: $@"
echo "Arg count: $#"
}
show_args a b c
# Function name: show_args
# First arg: a
# Second arg: b
# All args: a b c
# Arg count: 3
# pass all script args to a function
process "$@"
# shift within a function (local to it)
parse() {
while (($#)); do
echo "arg: $1"
shift
done
}戻り値と終了ステータス
Bash の 'return' は終了ステータス(0-255)のみを設定するため、関数はブールテストとしても機能します。文字列を返すには echo して $() で取得します。複数の値には nameref(local -n)— Bash 4.3+ — を使用します。これにより関数が呼び出し元の変数に名前で代入できます。これは複雑なデータを返す最もクリーンな方法です。関数を再入可能でなくするため、戻り値にグローバル変数を使用しないでください。
# return sets exit status (0-255), not a value
is_even() {
if (($1 % 2 == 0)); then
return 0 # true/success
else
return 1 # false/failure
fi
}
# use as a condition
if is_even 4; then
echo "4 is even"
fi
# chain with && and ||
is_even 4 && echo "yes" || echo "no"
# return a string via echo (capture with $())
to_upper() {
echo "${1^^}"
}
UP=$(to_upper hello) # HELLO
# return multiple values via global or nameref
set_pair() {
local -n _r=$1
_r=(10 20)
}
set_pair result
echo "${result[@]}" # 10 20変数スコープ
Bash の変数はデフォルトでグローバルです — 関数内で代入されても!これはバグの一般的な原因です。関数内部の変数には常に 'local' を使用してください。'declare -g' は関数内から明示的にグローバルを作成します。再帰は動作しますが Bash では遅い(各呼び出しが $() のためにサブシェルをフォーク)。控えめに使用してください。階乗の例がパターンを示します:ローカル変数 + 再帰呼び出し + 算術。
# global by default
x=1
modify() {
x=2 # changes the GLOBAL x!
local y=3 # local to function
echo "inside: x=$x y=$y"
}
modify
echo "outside: x=$x" # x=2 (modified!)
echo "outside: y=$y" # empty (y is local)
# declare -g for global inside a function
set_global() {
declare -g NEW_VAR=42
}
set_global
echo $NEW_VAR # 42
# recursion with local
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
echo $(factorial 5) # 120ライブラリとソース
source(または .)は現在のシェルでファイルを実行し、関数と変数を利用可能にします — これが再利用可能なライブラリの構築方法です。一般的なパターンは、スクリプトが source するヘルパー関数を含む utils.sh です。スクリプトの実行(サブシェルで実行)とは異なり、ソースされたコードは呼び出し元の環境を変更できます。これが .bashrc と .bash_profile の動作方法でもあります — シェル起動時にソースされます。
# utils.sh — a reusable library
#!/bin/bash
log() {
echo "[$(date +%H:%M:%S)] $*" >&2
}
die() {
log "ERROR: $*"
exit 1
}
confirm() {
read -p "$1 [y/N] " ans
[[ $ans =~ ^[Yy]$ ]]
}
# main.sh — use the library
source ./utils.sh # or . ./utils.sh
log "Starting script"
confirm "Continue?" || die "aborted"
log "Done"
# source vs execute
# source: runs in current shell (shares variables)
# ./script.sh: runs in a subshell (isolated)テキスト処理
grep — パターン検索
grep はパターンにマッチする行を見つけます。-i は大文字小文字を無視、-v は反転、-n は行番号表示、-r は再帰、-E は拡張正規表現(+、|、{} など)を使用します。-A/-B/-C はマッチの周囲のコンテキスト行を表示し — エラーの理解に不可欠です。再帰検索中にファイルタイプでフィルタするには --include を使用します。grep はマッチが見つかると終了ステータス0を返すため、条件で便利です:if grep -q pattern file; then...
# basic search
grep "error" app.log
grep -i "error" app.log # case-insensitive
grep -v "debug" app.log # invert (non-matching lines)
grep -c "error" app.log # count matches
grep -n "error" app.log # line numbers
grep -w "error" app.log # whole word only
# recursive search
grep -rn "TODO" src/ # recursive + line numbers
grep -rl "TODO" src/ # files with matches only
# extended regex (or use egrep)
grep -E "error|warn|fatal" log
grep -E "^[0-9]{4}-" log # lines starting with date
# context lines
grep -A 2 "error" log # 2 lines after
grep -B 2 "error" log # 2 lines before
grep -C 2 "error" log # 2 lines before and after
# search multiple files
grep "function" *.js --include="*.js" -r .sed — ストリームエディタ
sed はテキストストリームを非対話的に編集します。s コマンドは置換し、g はグーバルにします。-i はインプレース編集です(常に最初に -i なしでテストしてください!)。区切り文字は任意の文字にできます — スラッシュのエスケープを避けるためパスには | や # を使用します。sed は行ごとに処理するため、複数行操作には N コマンドやホールドスペースが必要です。複雑な編集には awk や perl の方が明確かもしれません。常に sed スクリプトを引用符で囲んでシェル展開を防いでください。
# substitute (replace)
sed 's/old/new/' file.txt # first occurrence per line
sed 's/old/new/g' file.txt # all occurrences (global)
sed 's/old/new/3' file.txt # 3rd occurrence only
sed 's/old/new/gi' file.txt # global + case-insensitive
# in-place editing
sed -i 's/foo/bar/g' file.txt # edit file in place
sed -i.bak 's/foo/bar/g' file.txt # keep a backup
# delete lines
sed '/^#/d' file.txt # delete comment lines
sed '/^$/d' file.txt # delete blank lines
sed '5d' file.txt # delete line 5
sed '5,10d' file.txt # delete lines 5-10
# print specific lines
sed -n '10,20p' file.txt # print lines 10-20
sed -n '/pattern/p' file.txt # print matching lines
# use a different delimiter for paths
sed 's|/usr/local|/opt|g' paths.txt
# multiple commands
sed -e 's/a/A/g' -e 's/b/B/g' file.txtawk — 列処理
awk は列データのためのミニプログラミング言語です。$1、$2... はフィールド、$0 は行全体、$NF は最後のフィールドです。-F は入力区切り文字を設定し、OFS は出力を設定します。BEGIN は処理前に、END は処理後に実行されます。NR はレコード(行)番号、NF はフィールド数です。awk は CSV/TSV 処理、ログ分析、レポート生成に理想的です — 単純な抽出を超えると cut よりはるかに強力です。
# print columns (default separator: whitespace)
awk '{print $1}' file.txt # first column
awk '{print $1, $3}' file.txt # columns 1 and 3
awk '{print $NF}' file.txt # last column
awk -F: '{print $1}' /etc/passwd # split on :
# filter and print
awk '$3 > 100' file.txt # lines where col 3 > 100
awk '$1 == "ERROR" {print $2}' log # col 2 of ERROR lines
awk 'NR == 5' file.txt # 5th line only
awk 'NR >= 10 && NR <= 20' file.txt # lines 10-20
# BEGIN and END blocks
awk 'BEGIN {print "Start"} {sum += $1} END {print sum}' nums.txt
# field separator and output
awk -F, 'BEGIN {OFS="|"} {print $1, $2}' csv.txt
# count lines matching a pattern
awk '/error/' file.txt # like grep
awk '/error/ {count++} END {print count}' log
# built-in variables: NR (row), NF (fields), FS, OFS
awk '{print NR, NF, $0}' file.txtcut、tr、sort と uniq
cut はシンプルなフィールド/文字抽出です — 高速だが制限あり(引用符サポートなし)。tr は文字を変換(大文字小文字変換や区切り文字の入れ替えに便利)し、-d は削除します。sort は行をソートします(-n は数値、-r は逆順、-k はフィールド)。uniq は隣接する重複のみを削除するため、常に最初に sort にパイプしてください。uniq -c と sort -rn が頻度分析の古典的パターンです:'sort | uniq -c | sort -rn' で最も一般的な行を表示します。
# cut: extract fields or characters
cut -d: -f1 /etc/passwd # field 1, delimiter :
cut -c1-5 file.txt # characters 1-5
cut -d, -f2,3 data.csv # fields 2 and 3
# tr: translate or delete characters
echo "Hello" | tr 'a-z' 'A-Z' # HELLO
echo "a,b,c" | tr ',' ' ' # a b c
echo "hello" | tr -d 'l' # heo (delete)
tr -s ' ' < file.txt # squeeze repeated spaces
# sort lines
sort file.txt # alphabetical
sort -n nums.txt # numeric
sort -rn nums.txt # reverse numeric
sort -t: -k3 -n /etc/passwd # by 3rd field, numeric
sort -u file.txt # sort + unique
# uniq: filter or count adjacent duplicates
sort file.txt | uniq # unique lines
sort file.txt | uniq -c # count occurrences
sort file.txt | uniq -d # only duplicates
sort file.txt | uniq -u # only unique linesパイプとリダイレクト
パイプは stdout を stdin に接続し、強力なパイプラインを構築します。> は stdout をリダイレクト(>> は追記)、2> は stderr、&> は両方を取得します。ヒアドキュメント(<<EOF)は複数行文字列を供給し、区切り文字を引用符で囲む('EOF')と変数展開を無効にします。プロセス置換 <(cmd) はコマンド出力を一時ファイルとして扱います — サブシェルなしで while ループに供給し、diff のようにファイルを期待するコマンドに不可欠です。
# pipe: chain commands (stdout of one -> stdin of next)
cat file | grep "x" | sort | uniq -c | sort -rn | head
# redirect stdout
echo "hello" > file.txt # overwrite
echo "world" >> file.txt # append
# redirect stderr
command 2> error.log # stderr to file
command 2>&1 # stderr to stdout
command > all.log 2>&1 # both to same file
command &> all.log # both (Bash shortcut)
# stdin from file
command < input.txt
# here-string
grep "x" <<< "some text with x"
# here-document (multi-line input)
cat << EOF
Line 1
Line 2 with $VAR expansion
EOF
# quoted delimiter disables expansion
cat << 'EOF'
Literal $VAR no expansion
EOF
# process substitution
diff <(ls dir1) <(ls dir2) # compare outputs
while read line; do echo "$line"; done < <(grep x file)ファイルとディレクトリ操作
find — ファイル検索
find は最も強力なファイル検索ツールです。-name は glob にマッチ(大文字小文字を無視するには -iname を使用)、-type は f/d/l でフィルタ、-mtime/-mmin は変更時間でフィルタ(- は以内、+ はより古い)、-size はサイズでフィルタします。-exec は各結果でコマンドを実行し、{} はファイル名、; はファイルごと、+ はバッチ化します。-delete は削除に -exec rm より安全です。常に最初に -delete なしで find をテストしてください!
# search by name
find . -name "*.js"
find . -iname "*.JS" # case-insensitive
find / -name "*.conf" 2>/dev/null # suppress permission errors
# search by type
find . -type f -name "*.txt" # files only
find . -type d -name src # directories only
find . -type l # symlinks
# search by time and size
find . -mtime -7 # modified < 7 days ago
find . -mtime +30 # modified > 30 days ago
find . -mmin -60 # modified < 60 min ago
find . -size +10M # larger than 10MB
find . -size -1k # smaller than 1KB
find . -empty # empty files/dirs
# act on results (-exec)
find . -name "*.log" -exec rm {} \;
find . -name "*.js" -exec wc -l {} +
find . -type f -exec chmod 644 {} \;
# safe deletion with -delete
find /tmp -type f -mtime +7 -deleteパーミッションと所有権
パーミッションは3つの三重項です:所有者、グループ、その他。各桁は r(4)+w(2)+x(1) なので、755 = rwxr-xr-x です。chmod はパーミッションを変更し、chown は所有者/グループを変更します。シンボリック表記(u+x、g-w)は段階的な変更に明確です。umask は新規ファイルのデフォルトパーミッションを設定します(ファイルは666から、ディレクトリは777から減算)。Web サーバーでは、ディレクトリは755、ファイルは644が標準です。本番で777は決して使用しないでください。
# view permissions
ls -l file.txt
# -rw-r--r-- 1 user group 1024 Jan 1 10:00 file.txt
# ^^^ ^^^ ^^^
# owner group others
# chmod: change permissions
chmod 755 script.sh # rwxr-xr-x
chmod 644 file.txt # rw-r--r--
chmod +x script.sh # add execute for all
chmod u+x,g-w file.txt # symbolic: user +x, group -w
chmod -R 755 directory/ # recursive
# chown: change owner
chown alice file.txt
chown alice:developers file.txt
chown -R alice:developers project/
# umask: default permissions for new files
umask # show current (e.g. 022)
umask 077 # new files: 600, dirs: 700
# numeric permission reference:
# 7=rwx 6=rw 5=rx 4=r 3=wx 2=w 1=x 0=noneコピー、移動、削除とリンク
cp -r はディレクトリを再帰的にコピーし、-i は上書き前にプロンプト(より安全)、-p はタイムスタンプとパーミッションを保持します。mv は移動とリネームの両方です。rm -rf は危険です — プロンプトなしで再帰的に削除します。常にパスを再確認してください。ハードリンクは同じ inode を指し(同じファイル、ファイルシステムを越えられない、ディレクトリをリンクできない)、シンボリックリンクはパス参照(何でもリンクできるが、ターゲットが移動すると壊れる)です。シンボリックリンクには ln -s を使用してください。