「ファイル名から拡張子を削除したい」 「パスの最後のスラッシュを取りたい」 「末尾の改行や空白を削除したい」
Linuxでは文字列の後ろから文字を削除する方法が 10種類以上もあるんです!
bashのパラメータ展開、sed、awk、cut… それぞれ得意な処理が違います。
この記事を読めば、 あらゆる末尾削除処理を 最適な方法で実装できるようになります!
Bashパラメータ展開(最速・最も簡単)

後ろから指定文字数を削除
# 基本構文:${変数:0:-削除文字数}
# 後ろから3文字削除
text="hello.txt"
echo ${text:0:-3} # hello.
# 後ろから4文字削除
filename="document.pdf"
echo ${filename:0:-4} # document
# 変数で文字数を指定
string="abcdefghij"
n=3
echo ${string:0:-$n} # abcdefg
# 実践例:タイムスタンプ削除
file="backup_20240115.tar.gz"
echo ${file:0:-17} # backup
パターンマッチで末尾削除
# %パターン:最短マッチを削除
# %%パターン:最長マッチを削除
# 拡張子を削除(最短マッチ)
file="archive.tar.gz"
echo ${file%.*} # archive.tar(.gzだけ削除)
echo ${file%%.*} # archive(.tar.gzすべて削除)
# 末尾のスラッシュを削除
path="/home/user/documents/"
echo ${path%/} # /home/user/documents
# 特定の文字列を削除
text="hello_world_2024"
echo ${text%_*} # hello_world(最短)
echo ${text%%_*} # hello(最長)
# 数字を削除
string="file123"
echo ${string%[0-9]} # file12(1文字)
echo ${string%%[0-9]*} # file(すべての数字)
実践的な使用例
#!/bin/bash
# 拡張子を変更する関数
change_extension() {
local file=$1
local new_ext=$2
echo "${file%.*}.${new_ext}"
}
# 使用例
change_extension "document.txt" "pdf" # document.pdf
# バッチ処理で拡張子削除
for file in *.jpg; do
name=${file%.jpg}
echo "処理中: $name"
# 何か処理
done
# パスからファイル名だけ取得
fullpath="/var/log/system.log"
filename=${fullpath##*/} # system.log
dirname=${fullpath%/*} # /var/log
sedコマンドで末尾削除
基本的な末尾削除
# 末尾のn文字を削除
echo "hello world" | sed 's/.\{3\}$//' # hello wo
# 末尾の1文字を削除
echo "test123" | sed 's/.$//' # test12
# 末尾の5文字を削除
echo "filename.txt" | sed 's/.\{4\}$//' # filename
# 複数行に対して処理
cat file.txt | sed 's/.\{2\}$//' # 各行の末尾2文字削除
# ファイルを直接編集
sed -i 's/.\{3\}$//' file.txt
パターンで末尾削除
# 末尾の数字を削除
echo "file123" | sed 's/[0-9]*$//' # file
# 末尾の空白を削除
echo "hello " | sed 's/[[:space:]]*$//' # hello
# 末尾の特定文字列を削除
echo "test.backup" | sed 's/\.backup$//' # test
# 拡張子を削除
echo "document.pdf" | sed 's/\.[^.]*$//' # document
# 末尾の改行を削除
echo -e "line1\n\n" | sed ':a;/^\n*$/{$d;N;ba}'
高度なsed処理
# 末尾の括弧内を削除
echo "function(param1, param2)" | sed 's/([^)]*)$//' # function
# URLのクエリパラメータを削除
echo "https://example.com/page?id=123" | sed 's/\?.*$//'
# 末尾のバージョン番号を削除
echo "app-v1.2.3" | sed 's/-v[0-9.]*$//' # app
# 条件付き削除(特定の行のみ)
sed '/^#/s/.\{2\}$//' file.txt # コメント行のみ末尾2文字削除
awkコマンドで末尾処理
substr関数で削除
# 後ろからn文字削除
echo "hello world" | awk '{print substr($0, 1, length($0)-3)}' # hello wo
# 変数を使った削除
echo "test12345" | awk -v n=3 '{print substr($0, 1, length($0)-n)}'
# フィールド単位で処理
echo "one two three" | awk '{$NF=substr($NF,1,length($NF)-2); print}'
# CSVの特定カラムの末尾処理
echo "a,b,c123" | awk -F, '{$3=substr($3,1,length($3)-3); print}' OFS=,
正規表現での削除
# gsubで末尾パターン削除
echo "file.txt" | awk '{gsub(/\.[^.]*$/, ""); print}' # file
# 末尾の数字削除
echo "test123" | awk '{gsub(/[0-9]+$/, ""); print}' # test
# 複数パターンの削除
echo "data_2024_backup" | awk '{
gsub(/_backup$/, "")
gsub(/_[0-9]{4}$/, "")
print
}'
cutコマンド(シンプルだが制限あり)
revと組み合わせて末尾削除
# 後ろから3文字削除(revで反転)
echo "hello world" | rev | cut -c4- | rev # hello wo
# 関数化
remove_last_n() {
echo "$1" | rev | cut -c$(($2+1))- | rev
}
remove_last_n "test12345" 3 # test12
# パイプラインで処理
cat file.txt | while read line; do
echo "$line" | rev | cut -c3- | rev
done
その他のコマンド

headコマンド
# バイト単位で削除(GNU head)
echo "hello world" | head -c -3 # hello wo
# 改行も考慮
echo -n "test123" | head -c -2 # test1
perlワンライナー
# 後ろから3文字削除
echo "hello world" | perl -pe 's/.{3}$//'
# 拡張子削除
echo "file.txt" | perl -pe 's/\.[^.]*$//'
# 末尾の空白削除
echo "text " | perl -pe 's/\s+$//'
pythonワンライナー
# 後ろから3文字削除
echo "hello world" | python3 -c "import sys; print(sys.stdin.read()[:-3])"
# より柔軟な処理
echo "test123" | python3 -c "
import sys
text = sys.stdin.read().strip()
print(text[:-3] if len(text) > 3 else '')
"
ファイル名の一括処理
拡張子の一括削除
#!/bin/bash
# 方法1:forループ
for file in *.txt; do
newname="${file%.txt}"
mv "$file" "$newname"
done
# 方法2:findとexec
find . -name "*.bak" -exec sh -c '
for file; do
mv "$file" "${file%.bak}"
done
' sh {} +
# 方法3:renameコマンド(perl版)
rename 's/\.txt$//' *.txt
# 方法4:mmv(要インストール)
mmv "*.txt" "#1"
安全なリネーム関数
#!/bin/bash
safe_trim_rename() {
local pattern="$1"
local trim_chars="$2"
for file in $pattern; do
[ ! -f "$file" ] && continue
# 末尾から指定文字数削除
newname="${file:0:-$trim_chars}"
# ファイルが既に存在する場合の処理
if [ -f "$newname" ]; then
echo "警告: $newname は既に存在します"
continue
fi
echo "リネーム: $file → $newname"
mv "$file" "$newname"
done
}
# 使用例
safe_trim_rename "*.backup" 7
特殊なケース
改行・空白の削除
# 末尾の改行を削除
text=$(echo -e "hello\n\n")
echo -n "$text" | sed 's/\n*$//'
# 末尾の空白と改行を削除
echo " text " | sed 's/[[:space:]]*$//'
# tr コマンドで改行削除
echo -e "line1\nline2\n" | tr -d '\n'
# 末尾の空白行を削除
sed -i '/^[[:space:]]*$/d' file.txt
特定の区切り文字まで削除
# 最後のスラッシュ以降を削除
path="/home/user/documents/file.txt"
echo ${path%/*} # /home/user/documents
# 最後のドット以降を削除(拡張子)
file="archive.tar.gz"
echo ${file%.*} # archive.tar
# 最後のアンダースコア以降を削除
name="project_v1_final"
echo ${name%_*} # project_v1
パフォーマンス比較
#!/bin/bash
# テストデータ
string="This is a test string with some text"
iterations=10000
# Bashパラメータ展開(最速)
time for i in $(seq 1 $iterations); do
result=${string:0:-5}
done
# sed(遅い)
time for i in $(seq 1 $iterations); do
result=$(echo "$string" | sed 's/.....$//')
done
# awk(中間)
time for i in $(seq 1 $iterations); do
result=$(echo "$string" | awk '{print substr($0,1,length($0)-5)}')
done
よくあるエラーと対処法
エラー1:空文字列での処理
# ❌ エラーになる可能性
text=""
echo ${text:0:-3} # エラー
# ✅ 安全な処理
text=""
if [ -n "$text" ] && [ ${#text} -gt 3 ]; then
echo ${text:0:-3}
else
echo "$text"
fi
エラー2:文字数が足りない
# ❌ 問題のあるコード
text="ab"
echo ${text:0:-5} # エラーまたは予期しない結果
# ✅ 長さチェック付き
trim_end() {
local string="$1"
local n="$2"
if [ ${#string} -gt $n ]; then
echo "${string:0:-$n}"
else
echo ""
fi
}
trim_end "ab" 5 # 空文字列を返す
まとめ:用途別ベストプラクティス
単純な文字数削除:
${variable:0:-n} # Bashパラメータ展開が最速
パターンマッチ削除:
${variable%pattern} # 拡張子削除など
複数行処理:
sed 's/.{n}$//' file.txt # ファイル全体を処理
複雑な条件:
awk '{条件処理}' file # 柔軟な処理が可能
大量ファイルの処理:
find + xargs または parallel # 並列処理で高速化
文字列の末尾削除をマスターすれば、 シェルスクリプトが格段に便利になります!
用途に応じて最適な方法を選んで、 効率的な文字列処理を実現しましょう。
コメント