Bashで文字列を切り出す方法|部分文字列・末尾取得・cutとの使い分けを解説

スクリプト設計

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やコロン区切りのデータから特定のフィールドを取り出すには cutawk が便利だ。

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}   # 日本

日本語を含む文字列を切り出す場合は、ロケールを明示するか awksubstr を使う方が安全。

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。用途に応じて使い分けることで、シンプルで読みやすいスクリプトが書ける。

関連記事:

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする

コメント