Linux / コマンド / dirname - パスからディレクトリ部分を抽出

dirnameは、パス文字列から最後のファイル名やディレクトリ名を削除し、その前のディレクトリ部分を出力するコマンドです。シェルスクリプトでファイルの上位パスを取得したり、basenameと一緒にパスを分割して処理するときに便利です。

dirnameは、実際のファイルシステムを探索したり、上位ディレクトリの存在を確認したりしません。入力文字列に決められたルールを適用するため、実際の絶対パスやシンボリックリンクの解釈が必要な場合は、realpathのようなコマンドを使用する必要があります。

dirnameコマンドとは?

全パスから最後の構成要素を除いた部分を得るには、次のように実行します。

dirname /home/user/report.txt

出力は次の通りです。

/home/user

入力した最後の名前が実際にファイルかディレクトリかは確認しません。単純な文字列処理なので、存在しないパスにも同じルールが適用されます。

基本的な文法と使い方

dirname 名前 ...

GNU dirnameは、複数のパスを受け取り、それぞれの結果を1行ずつ出力できます。

dirname /var/log/nginx/access.log
dirname /home/user/Documents/
dirname relative/path/file.txt

一般的な結果は、それぞれ/var/log/nginx、/home/user、relative/pathです。末尾のスラッシュや連続したスラッシュは整理過程で変わることがあるため、元の文字列をそのまま保存する用途には使用しないでください。

入力形式別の結果

入力 一般的な出力
/home/user/report.txt /home/user
docs/manual.pdf docs
notes.txt .
/usr/ /
/ /

スラッシュのない名前

入力にスラッシュがなければ、カレントディレクトリを意味するドット(.)を出力します。

dirname report.txt
.

この結果は、report.txtというファイルが実際にカレントディレクトリに存在することを意味するのではなく、入力文字列のディレクトリ部分を表現したものです。

絶対パスとルートパス

絶対パスの最後の構成要素を削除してルートだけが残る場合、/を出力します。

dirname /etc
dirname /

どちらのコマンドも、通常/を出力します。ルートの親を文字列で表現しても、ルート自体です。

パスの末尾のスラッシュ

最後の不要なスラッシュはまず削除され、その後に最後の構成要素が処理されます。

dirname /srv/www/site/
dirname /srv/www/site///

両方の入力は通常 /srv/www を出力します。元のスラッシュの数や末尾のスラッシュを保持する操作には適していません。

複数のパスを処理する

複数の名前を一度に指定する

GNU 実装では、複数のパスを列挙することができます。

dirname /srv/www/index.html /var/log/syslog /home/user/file.txt

各入力のディレクトリ部分が1行ずつ出力されます。

/srv/www
/var/log
/home/user

-zで結果をNUL文字で区切る

ファイル名に改行が含まれる可能性がある自動化では、-z または --zero で各結果の末尾をNUL文字(値が0の区切り文字)にできます。

dirname -z /srv/www/index.html /var/log/syslog

このオプションは GNU 拡張ですので、他の Unix 系システムでも実行する場合は、サポートされているか確認してください。

dirname と basename を一緒に使用する

1つのパスからディレクトリ部分と最後の名前をそれぞれ抽出できます。最後の名前を取り出す処理はbasenameコマンドの使い方でも詳しく説明しています。

path='/home/user/docs/report.pdf'
directory=$(dirname -- "$path") || exit 1
name=$(basename -- "$path") || exit 1

printf 'ディレクトリ: %s\n' "$directory"
printf '名前: %s\n' "$name"

出力は次のような形になります。

ディレクトリ: /home/user/docs
名前: report.pdf

コマンド置換 $(...) は出力末尾の改行を取り除くため、名前の末尾に改行がある場合にはこの変数代入で元の名前を保てません。2つのコマンドは、元の文字列を意味のある2つの部分に分割するのに便利ですが、結果を再び結合しても元の重複スラッシュや末尾スラッシュまで復元されるわけではありません。

シェルスクリプトでの活用方法

出力ファイルの上位ディレクトリを作る

ファイルを作成する前に上位ディレクトリを準備できます。次のパスは例なので、自分が書き込める出力先に変更してください。mkdir -p の終了状態も確認し、成功した場合にファイル作成へ進みます。

output='/srv/backups/daily/archive.tar.gz'
output_dir=$(dirname -- "$output") || exit 1
mkdir -p -- "$output_dir"

入力パスを信頼できない場合は、希望する基準ディレクトリの外に出ないかどうかを別途検証する必要があります。dirname自体はアクセス範囲を制限しません。

POSIXシェルパラメータ展開と比較する

単純な入力であれば、シェルパラメータ展開でスラッシュの後の最後の部分を削除できます。

path='/srv/www/index.html'
directory=${path%/*}
printf '%s\n' "$directory"

しかし、スラッシュがない名前、ルートパス、末尾にスラッシュがあるパスではdirnameと結果が異なる場合があります。入力の形式が多様であれば、自作の展開式の例外を十分に試してください。

dirname、realpathとpwdの違い

コマンド 処理対象 主な結果
dirname 入力したパス文字列 最後の名前を除いた文字列
basename 入力したパス文字列 最後のファイル名またはディレクトリ名
realpath 指定したパスとファイルシステム 正規化されたまたはシンボリックリンクが解釈されたパス
pwd 現在のプロセスの作業ディレクトリ 現在の作業ディレクトリの絶対パス

現在の作業場所を確認する場合はpwdコマンドの使い方が参考になります。

dirname ../logs/app.logは文字列として../logsを出力します。これは現在の位置基準の絶対パスに変換したり、シンボリックリンクを解釈することはありません。

主なオプションまとめ

オプション 説明
-z, --zero 各結果を改行ではなくNUL文字で終わらせます。
--help 使用方法とサポートオプションを出力します。
--version インストールされているGNU Coreutilsのバージョンを出力します。
-- 以降の引数はオプションではなく名前として処理されます。

よく発生する問題

結果がドット1つで出力される

入力された名前にスラッシュが含まれていないためです。dirname file.txtの結果である.は文字列の観点から現在のディレクトリを示します。

上位ディレクトリは実際には存在しません

dirnameはファイルシステムをチェックしません。出力されたディレクトリを実際に利用する場合は、存在の有無やアクセス権を別途確認する必要があります。

空白やワイルドカードを含むパスが壊れる

変数を二重引用符で囲み、ハイフンで始まる値には--を使用してください。

dirname -- "$path"

FAQ

dirnameは実際に上位ディレクトリを探しますか?

いいえ。入力したパス文字列から最後の要素を削除するだけで、実際のパスの存在やシンボリックリンクを確認することはありません。

dirname file.txtがピリオド1つを出力する理由は何ですか?

入力にディレクトリを区切るスラッシュがないため、現在のディレクトリを表す.を出力します。

複数のパスを一度に処理できますか?

GNU dirnameは複数のパスを引数として受け取り、それぞれの結果を出力します。他のUnix実装との移植性が必要な場合は、まずサポート形式を確認してください。

絶対パスが必要な場合、dirnameを使用してもよいですか?

dirnameは相対パスを絶対パスに変換しません。正規化された絶対パスが必要な場合は、目的に応じたrealpathオプションを使用してください。

参考資料