Bashで文字列の一部を取り出したい場面は多い。ログの解析、ファイル名の加工、CSVの特定フィールド取得など、実務でほぼ毎日使う操作だ。
Bashには文字列を切り出すための方法が複数ある。まず最初に基本形を押さえておこう。
str="Hello, Bash!"
# 先頭から5文字
echo ${str:0:5} # Hello
# 7文字目以降すべて
echo ${str:7} # Bash!
# 末尾から5文字
echo ${str: -5} # Bash!
この記事では、Bashの変数展開による部分文字列の取り出し方を中心に、cut / awk / sed との使い分け、ファイル名・拡張子の処理、よくある失敗まで整理する。
Bashで文字列を切り出す方法の全体像
Bashで文字列を切り出す手段は大きく4つに分類できる。
| 手段 | 構文例 | 向いているケース |
|---|---|---|
| 変数展開(部分文字列) | ${var:pos:len} |
位置・文字数で切り出す |
| パラメータ展開(削除) | ${var#pattern} |
パターンに一致する前後を削除 |
| cutコマンド | cut -d: -f2 |
区切り文字で特定フィールドを取得 |
| awkコマンド | awk -F: '{print $2}' |
複数フィールドの加工・条件付き処理 |
開始位置と文字数を指定して切り出す
Bashの変数展開 ${var:開始位置:長さ} を使うと、文字列の任意の位置から任意の文字数を取り出せる。位置は0始まりであることに注意。
str="abcdefgh"
# 先頭(位置0)から3文字
echo ${str:0:3} # abc
# 位置3から4文字
echo ${str:3:4} # defg
先頭から切り出す
先頭から N 文字を取り出す場合、開始位置は 0 になる。
filename="report_2024.csv"
# 先頭6文字
echo ${filename:0:6} # report
途中から末尾まで切り出す
長さを省略すると、指定位置から末尾まで取り出せる。
str="Hello, Bash!"
# 7文字目(インデックス7)以降
echo ${str:7} # Bash!
末尾から切り出す
開始位置にマイナス値を使うと、末尾から数えた位置を指定できる。ただし ${var: -3} のようにコロンとマイナスの間にスペースが必要。スペースがないと ${var:-3}(デフォルト値の展開)と解釈されてしまう。
str="Hello, Bash!"
# 末尾から5文字
echo ${str: -5} # Bash!
# 末尾から5文字のうち先頭4文字(末尾から5文字目から4文字分)
echo ${str: -5:4} # Bash
# NG: スペースなしはデフォルト値展開になる
echo ${str:-3} # Hello, Bash!(strが空のときだけ "3" になる)
文字列長を取得する
${#var} で変数の文字列長(バイト数ではなく文字数)を取得できる。末尾から切り出す位置の計算にも使える。
str="Hello"
echo ${#str} # 5
# 文字列長を使った動的な処理
last3=${str: -3}
echo $last3 # llo
# 文字列長から特定の範囲を計算
len=${#str}
echo ${str:$((len-3)):3} # llo
マルチバイト文字(日本語など)を扱う場合は注意が必要。${#var} は文字数ではなくバイト数を返すことがある(ロケール依存)。詳しくは後述の「よくある失敗例」を参照。
パラメータ展開による前方・後方削除
パターンに一致する部分を削除して残りを取り出す方法。位置ではなくパターンで切り出したいときに便利だ。
前方削除:${var#pattern} / ${var##pattern}
| 構文 | 説明 |
|---|---|
${var#pattern} |
先頭から最短一致を削除 |
${var##pattern} |
先頭から最長一致を削除 |
path="/home/user/docs/report.txt"
# 最短一致: 最初の / まで削除
echo ${path#/} # home/user/docs/report.txt
# 最長一致: 最後の / まで削除(ファイル名だけ取得)
echo ${path##*/} # report.txt
str="aaa_bbb_ccc"
# 最短一致: 最初の _ まで削除
echo ${str#*_} # bbb_ccc
# 最長一致: 最後の _ まで削除
echo ${str##*_} # ccc
後方削除:${var%pattern} / ${var%%pattern}
| 構文 | 説明 |
|---|---|
${var%pattern} |
末尾から最短一致を削除 |
${var%%pattern} |
末尾から最長一致を削除 |
path="/home/user/docs/report.txt"
# 最短一致: 最後の . 以降を削除(拡張子を除くファイル名)
echo ${path%.*} # /home/user/docs/report
# 最長一致: 最初の . 以降をすべて削除
echo ${path%%.*} # /home/user/docs/report
str="aaa_bbb_ccc"
# 最短一致: 最後の _ 以降を削除
echo ${str%_*} # aaa_bbb
# 最長一致: 最初の _ 以降をすべて削除
echo ${str%%_*} # aaa
ファイル名・拡張子の処理
パラメータ展開はファイルパスの加工で特に活躍する。
ファイル名だけ取得する
path="/home/user/docs/report.txt"
# ファイル名(拡張子あり)
filename="${path##*/}"
echo $filename # report.txt
# bash組み込みの代替
echo $(basename "$path") # report.txt
拡張子を除くベース名を取得する
filename="report.tar.gz"
# 最短一致: 最後の拡張子だけ除く
echo ${filename%.*} # report.tar
# 最長一致: 最初のドット以降をすべて除く
echo ${filename%%.*} # report
拡張子だけ取得する
filename="report.txt"
# 最後のドット以降を取得
ext="${filename##*.}"
echo $ext # txt
# ドットを含めて取得したい場合
ext=".${filename##*.}"
echo $ext # .txt
ディレクトリ部分だけ取得する
path="/home/user/docs/report.txt"
# ディレクトリパス(最後のスラッシュ以降を除く)
dir="${path%/*}"
echo $dir # /home/user/docs
# bash組み込みの代替
echo $(dirname "$path") # /home/user/docs
区切り文字で切り出す
CSVやコロン区切りのデータから特定のフィールドを取り出すには cut や awk が便利だ。
cutコマンドで切り出す
cut -d で区切り文字を指定し、-f でフィールド番号を指定する。フィールド番号は1始まり。
line="Alice:30:engineer"
# 1番目のフィールド
echo "$line" | cut -d: -f1 # Alice
# 2番目のフィールド
echo "$line" | cut -d: -f2 # 30
# 2番目から3番目のフィールド
echo "$line" | cut -d: -f2-3 # 30:engineer
# カンマ区切りのCSV
csv="2024,01,15,Tokyo"
echo "$csv" | cut -d, -f3 # 15
ファイルの各行に対して適用することもできる。
# /etc/passwd の1フィールド目(ユーザー名)を取得
cut -d: -f1 /etc/passwd
cutコマンドの詳細は cutコマンド解説記事 も参照。
awkで切り出す
awk -F で区切り文字を指定し、$1、$2 のようにフィールドを参照する。cutより柔軟で、条件付き処理や演算も可能だ。
line="Alice:30:engineer"
# 1番目のフィールド
echo "$line" | awk -F: '{print $1}' # Alice
# 複数フィールドを組み合わせる
echo "$line" | awk -F: '{print $1, "is", $3}' # Alice is engineer
# 条件付き(30歳以上のみ)
echo "$line" | awk -F: '$2 >= 30 {print $1}' # Alice
# 空白区切り(デフォルト)
echo "hello world" | awk '{print $2}' # world
awkの詳細は awkコマンド解説記事 も参照。
cut / awk / sed / Bash変数展開の使い分け
どれを使うべきか迷ったときの判断基準を整理する。
| 手段 | 得意な場面 | 苦手な場面 |
|---|---|---|
Bash変数展開${var:pos:len} |
スクリプト内の変数処理、サブプロセス不要で高速 | 複数フィールドの処理、ファイルへの直接適用 |
パラメータ展開${var#pat} |
ファイル名・拡張子の処理、パターンで前後を削除 | 複雑な正規表現、数値計算 |
| cut | 固定の区切り文字でフィールドを取得、シンプルで速い | 複数区切り文字、条件付き処理、フィールドの演算 |
| awk | 複数フィールドの加工、条件付き処理、演算 | 単純な切り出しには冗長になることも |
| sed | 正規表現による置換・削除、ファイルのインプレース編集 | フィールド単位の操作、数値演算 |
選び方のポイント:
- スクリプト変数の加工 → Bash変数展開(サブシェル不要で最速)
- ファイル名・拡張子の処理 → パラメータ展開
- 区切り文字でフィールドを切り出す → まず cut、条件付きなら awk
- 正規表現で部分抽出・置換 → sed または grep -oP
sedの詳細は sedコマンド解説記事 も参照。
よくある失敗例
位置指定が1始まりだと思い込む
Bashの ${var:pos:len} はインデックスが 0始まり。1文字目は位置0。
str="abcde"
# NG: 1始まりと思って使うと1文字ずれる
echo ${str:1:3} # bcd(2〜4文字目)
# OK: 先頭3文字は :0:3
echo ${str:0:3} # abc
末尾からの切り出しでスペースを忘れる
${var:-3} はデフォルト値展開(varが空なら “3” を返す)。末尾からの切り出しには ${var: -3}(スペース必要)。
str="Hello"
# NG: デフォルト値展開になってしまう
echo ${str:-3} # Hello(strが空でないのでstrの値がそのまま返る)
# OK: スペースを入れる
echo ${str: -3} # llo
マルチバイト文字で想定と違う結果になる
日本語などのマルチバイト文字は、ロケール設定によって ${#var} がバイト数を返すことがある。
# LC_ALL=C の場合(バイト単位)
LC_ALL=C str="日本語"
echo ${#str} # 9(UTF-8では1文字3バイト)
echo ${str:0:3} # 日(3バイト = 1文字)
# LC_ALL=ja_JP.UTF-8 の場合(文字単位)
LC_ALL=ja_JP.UTF-8 str="日本語"
echo ${#str} # 3
echo ${str:0:2} # 日本
日本語を含む文字列を切り出す場合は、ロケールを明示するか awk の substr を使う方が安全。
cutで区切り文字を指定し忘れる
cut -f2 だけだとデフォルトの区切り文字(タブ)で分割される。意図しない結果になることが多い。
line="Alice:30:engineer"
# NG: タブ区切りで分割されるため、フィールドが1つしかない
echo "$line" | cut -f2 # (何も出力されないか行全体が出力される)
# OK: -d で区切り文字を明示
echo "$line" | cut -d: -f2 # 30
ファイル名・拡張子の処理でパターンを誤る
${var%.*}(最短一致)と ${var%%.*}(最長一致)の使い分けを間違えると想定外の結果になる。
file="archive.tar.gz"
# 最後の拡張子だけ除く(最短一致)
echo ${file%.*} # archive.tar
# すべての拡張子を除く(最長一致)
echo ${file%%.*} # archive
# 最後の拡張子だけ取得
echo ${file##*.} # gz
よくある疑問
Bashで文字列の一部を取り出すにはどうするか
${var:開始位置:長さ} を使う。開始位置は0始まり。長さを省略すると指定位置から末尾まで取り出せる。
str="Hello, World!"
echo ${str:7:5} # World
echo ${str:7} # World!
末尾から数文字を取得するにはどうするか
${var: -N}(コロンとマイナスの間にスペース)を使う。
str="Hello, World!"
echo ${str: -6} # orld!
echo ${str: -6:5} # orld
区切り文字で分割するにはどうするか
単純なフィールド取得なら cut -d区切り文字 -fN、複数フィールドの加工や条件付きなら awk -F区切り文字 を使う。
data="2024:01:15"
# cutで2番目のフィールド
echo "$data" | cut -d: -f2 # 01
# awkで複数フィールドを結合
echo "$data" | awk -F: '{print $1 "/" $2 "/" $3}' # 2024/01/15
ファイル名から拡張子を取り出すにはどうするか
パラメータ展開の最長一致を使う。
filename="document.pdf"
ext="${filename##*.}"
echo $ext # pdf
cutとBash変数展開はどう使い分けるか
スクリプト内の変数を位置や文字数で切り出すなら Bash変数展開(高速、サブシェル不要)。コマンドのパイプライン処理やファイルの各行から特定フィールドを取り出すなら cut または awk が適している。
まとめ
Bashで文字列を切り出す方法を整理した。
${var:pos:len}— 位置と文字数で切り出す(0始まり)${var: -N}— 末尾からN文字(スペース必須)${#var}— 文字列長の取得${var#pat}/${var##pat}— 前方削除(最短/最長一致)${var%pat}/${var%%pat}— 後方削除(最短/最長一致)cut -d区切り文字 -fN— 区切り文字でフィールドを取得awk -F区切り文字 '{print $N}'— 柔軟なフィールド処理
スクリプト内の変数処理にはBashの変数展開が最速。区切り文字でのフィールド取得にはcutやawk。正規表現が必要なときはsed。用途に応じて使い分けることで、シンプルで読みやすいスクリプトが書ける。
関連記事:

コメント