「ディレクトリ部分だけを変数に入れて処理したいが、sedやawkでのパス加工は複雑すぎる」
サーバー運用の現場でシェルスクリプトを書いていると、フルパスからファイル名や親ディレクトリを取り出す操作は日常茶飯事です。そのたびに正規表現を書いていては効率が悪く、バグの温床にもなります。
この記事では、
basename(ベースネーム) と dirname(ダーネーム) コマンドの実践的な使い方を解説します。基本的なパス分解から、拡張子の除去、for ループや find との組み合わせ、スクリプト内での自己パス取得まで、20年以上Linuxサーバーを運用してきた経験から、現場で実際に使えるパターンをまとめました。
動作確認環境:RHEL 9.4 / Ubuntu 24.04 LTS
この記事のポイント
・basename はパスの最後の要素(ファイル名)を取り出す
・dirname はパスのディレクトリ部分を取り出す
・basename に拡張子を指定すると拡張子なしのファイル名を得られる
・スクリプト内の自己パス取得には dirname "$0" が定番
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
basename と dirname の役割を理解する
シェルスクリプトでパスを扱う際、basename と dirname は対になるコマンドです。たとえば
/var/log/httpd/access_log というパスに対して:・basename:パスの末尾要素(ファイル名)を返す →
access_log・dirname:パスのディレクトリ部分を返す →
/var/log/httpdどちらも GNU coreutils に含まれており、RHEL / Ubuntu / Debian / AlmaLinux などほぼ全てのLinuxディストリビューションで標準インストールされています。
basename コマンドの基本的な使い方
1. ファイル名を取り出す
最も基本的な使い方です。フルパスを引数として渡すと、ディレクトリ部分を除いたファイル名が返ります。# フルパスからファイル名だけを取得 $ basename /var/log/httpd/access_log access_log # 末尾にスラッシュがあっても正常に動作する $ basename /var/log/httpd/ httpd # ファイル名だけのパスを渡すとそのまま返る $ basename access_log access_log
2. 拡張子を除いたファイル名を取得する
第2引数に拡張子を指定すると、拡張子を取り除いたファイル名が返ります。バックアップスクリプトや変換スクリプトでファイル名のベース部分だけが必要な場合に便利です。# .log 拡張子を除いたファイル名を取得 $ basename /var/log/httpd/access_log .log access # .tar.gz の場合は1段ずつ除去する(2回実行が確実) $ basename /backup/server_2026-05-20.tar.gz .gz server_2026-05-20.tar $ basename server_2026-05-20.tar .tar server_2026-05-20
.tar.gz のような複合拡張子は1回の basename では取り除けません。2段階で処理するか、後述の変数展開(${var%.*})を組み合わせる方法があります。3. -a オプションで複数ファイルを一括処理(GNU版のみ)
GNU coreutils のbasename は -a オプションで複数のパスをまとめて処理できます。# 複数パスを一括でファイル名取得(GNU bash版) $ basename -a /etc/hosts /etc/passwd /etc/group hosts passwd group
-a オプションは挙動が異なります。移植性が必要なスクリプトではループ処理を使うほうが安全です。dirname コマンドの基本的な使い方
1. ディレクトリパスを取り出す
dirname はパスからディレクトリ部分を返します。# フルパスからディレクトリ部分を取得 $ dirname /var/log/httpd/access_log /var/log/httpd # ファイル名だけを渡すと . (カレントディレクトリ) を返す $ dirname access_log . # 末尾スラッシュつきのディレクトリ $ dirname /var/log/httpd/ /var/log # ルートパスの場合 $ dirname /etc/hosts /etc
2. dirname の特殊なケース
ルートディレクトリやスラッシュのみを渡した場合の動作を把握しておきましょう。# ルートを渡すとルートが返る $ dirname / / # 相対パスも正しく処理される $ dirname logs/access_log logs $ dirname ../config/settings.conf ../config
シェルスクリプトでの実践活用パターン
1. スクリプト自身のディレクトリを取得する
シェルスクリプト内で「このスクリプト自身が置かれているディレクトリ」を取得する定番パターンです。スクリプトをどこから実行しても正しいパスを参照できるため、設定ファイルや補助スクリプトの読み込みに非常によく使います。#!/bin/bash # スクリプト自身のディレクトリを取得するイディオム SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) echo "スクリプトの場所: ${SCRIPT_DIR}" # 同じディレクトリの設定ファイルを読み込む source "${SCRIPT_DIR}/config.sh"
dirname "$0" だけでは相対パスが返ることがあるため、cd && pwd を組み合わせて絶対パスを確定させるのが鉄板です。2. for ループで拡張子変換(バッチ処理)
ディレクトリ内の複数ファイルを別の拡張子に変換するバッチスクリプトのパターンです。#!/bin/bash # /backup/ 以下の .log ファイルを .txt にコピーする例 SRC_DIR="/var/log/httpd" DST_DIR="/backup/logs" for filepath in "${SRC_DIR}"/*.log; do # ファイル名(拡張子あり)を取得 filename=$(basename "${filepath}") # 拡張子を除いたベース名を取得 basename_no_ext=$(basename "${filepath}" .log) echo "処理中: ${filename} -> ${basename_no_ext}.txt" cp "${filepath}" "${DST_DIR}/${basename_no_ext}.txt" done
処理中: access_log -> access.txt 処理中: error_log -> error.txt 処理中: ssl_access_log -> ssl_access.txt
3. find と組み合わせてファイル名だけを使う
find で取得したフルパスからファイル名だけを使いたい場合の組み合わせです。# find で .conf ファイルを探し、ファイル名のみを表示する $ find /etc -name "*.conf" -type f | while read -r filepath; do echo "$(basename "${filepath}")" done # 実行結果(例) httpd.conf ssl.conf resolv.conf nsswitch.conf
find -printf "%f\n" という方法もありますが、basename を使うほうが可読性が高く、後続処理と組み合わせやすいため実務では好まれます。find コマンドとの組み合わせは Linux 基本コマンドの解説 も参考にしてください。4. バックアップスクリプトでの活用
バックアップ先のディレクトリを動的に作成し、元ファイルと同じ名前で保存するスクリプトの例です。#!/bin/bash # 設定ファイルをタイムスタンプ付きでバックアップする TARGET_FILE="$1" # 例: /etc/nginx/nginx.conf if [[ -z "${TARGET_FILE}" ]]; then echo "使い方: $0 <バックアップするファイルパス>" exit 1 fi # ファイル名とディレクトリを取得 fname=$(basename "${TARGET_FILE}") fdir=$(dirname "${TARGET_FILE}") # バックアップ先ディレクトリを作成 BACKUP_DIR="/backup$(fdir)" mkdir -p "${BACKUP_DIR}" # タイムスタンプ付きでコピー TIMESTAMP=$(date +%Y%m%d_%H%M%S) cp "${TARGET_FILE}" "${BACKUP_DIR}/${fname}.${TIMESTAMP}.bak" echo "バックアップ完了: ${BACKUP_DIR}/${fname}.${TIMESTAMP}.bak"
/etc/nginx/nginx.conf を引数に渡して実行した場合の出力です:# ./backup.sh /etc/nginx/nginx.conf を実行した場合 バックアップ完了: /backup/etc/nginx/nginx.conf.20260520_142301.bak
変数展開との使い分け
bash の変数展開機能(${var##*/} や ${var%/*})でも似た操作ができます。それぞれの使い分けを理解しておくと便利です。| やりたいこと | コマンド・変数展開 | 特徴 |
|---|---|---|
| ファイル名を取得 | basename "$path" |
可読性が高い。外部コマンド呼び出し |
| ファイル名を取得(変数展開) | ${path##*/} |
サブシェル不要。高速だが可読性が低い |
| ディレクトリを取得 | dirname "$path" |
可読性が高い。外部コマンド呼び出し |
| ディレクトリを取得(変数展開) | ${path%/*} |
サブシェル不要。高速だが可読性が低い |
| 拡張子を除いたファイル名 | basename "$path" ".${ext}" |
拡張子が固定の場合に明確 |
| 拡張子を除いたファイル名(変数展開) | ${filename%.*} |
任意の拡張子に対応。サブシェル不要 |
判断基準: ループ内で何千回も呼び出す場合は変数展開(サブシェル不要で高速)、保守性を重視するスクリプトでは
basename / dirname を使うのが現場の一般的な方針です。応用・実務 Tips
1. シンボリックリンクに注意する
dirname "$0" でシンボリックリンクが絡む場合、リンク先ではなくリンク元のパスが返ることがあります。スクリプトの場所を確実に解決したい場合は readlink -f を使います。#!/bin/bash # シンボリックリンクも解決した絶対パスでスクリプトディレクトリを取得 REAL_PATH=$(readlink -f "$0") SCRIPT_DIR=$(dirname "${REAL_PATH}") echo "スクリプトの実体パス: ${REAL_PATH}" echo "スクリプトのディレクトリ: ${SCRIPT_DIR}"
2. スペースや特殊文字を含むパスへの対処
パスにスペースや特殊文字が含まれる場合、必ずダブルクォートで囲みます。これはシェルスクリプトの基本中の基本ですが、basename / dirname でも例外ではありません。# NG: クォートなし(スペース入りパスでエラー) path="/home/user/My Documents/report.txt" basename $path # 「Documents/report.txt」などで誤動作 # OK: ダブルクォートで囲む basename "$path" # report.txt dirname "$path" # /home/user/My Documents
3. ls と組み合わせてディレクトリ構成を整理する
ls コマンドで取得したパスリストから、ファイル名だけを整形表示する例です。ls コマンドのオプション一覧は ls コマンドの基本オプション を参照してください。# /etc/cron.d/ のファイル名だけをリストアップ $ for f in /etc/cron.d/*; do echo "$(basename "$f")" done # 実行結果(例) 0hourly dailyjobs raid-check sysstat
よくあるエラーと対処法
「basename: missing operand」が出た時の対処
引数を渡さずに実行した場合に出るエラーです。変数が空になっていないか確認します。# エラーが出る例(変数が空) $ MYPATH="" $ basename "$MYPATH" basename: missing operand Try 'basename --help' for more information. # 対策:スクリプト内で空チェックを行う if [[ -z "${MYPATH}" ]]; then echo "エラー: パスが指定されていません" exit 1 fi basename "$MYPATH"
相対パスを渡した場合の dirname の挙動
相対パスを dirname に渡すと、相対パスのディレクトリ部分が返ります。期待した絶対パスが返らない場合はrealpath で先に絶対パスに変換してから処理します。# 相対パスを渡すと相対ディレクトリが返る $ dirname logs/access_log logs # realpath で絶対パスに変換してから dirname に渡す $ dirname "$(realpath logs/access_log)" /var/log/httpd # realpath が使えない環境では readlink -f で代替 $ dirname "$(readlink -f logs/access_log)" /var/log/httpd
本記事のまとめ
| やりたいこと | コマンド |
|---|---|
| ファイル名を取り出す | basename /path/to/file.txt |
| 拡張子を除いたファイル名を取り出す | basename /path/to/file.txt .txt |
| ディレクトリ部分を取り出す | dirname /path/to/file.txt |
| スクリプト自身のディレクトリを取得する | dirname "$0"(絶対パスは cd && pwd と組み合わせ) |
| シンボリックリンクを解決してディレクトリを取得する | dirname "$(readlink -f "$0")" |
| 複数ファイルのファイル名を一括取得(GNU版) | basename -a /path1 /path2 |
| 変数展開でファイル名を取得(高速) | ${path##*/} |
| 変数展開でディレクトリを取得(高速) | ${path%/*} |
Linux無料マニュアルを受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:yum・dnfコマンドでパッケージを管理する方法|インストールからリポジトリ設定まで
- この記事の属するカテゴリ:Linuxtips・LinuxコマンドA-Eへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます