こちら(シェルスクリプトで相対パスを絶対パスに変換する正しい方法)の記事の続きです。
相対パスを絶対パスに変換する方法
# set -e していれば 下記の || exit $? は不要
dirname="$(cd -- "$(dirname -- "$1")" && pwd)" || exit $?
abspath="${dirname%/}/$(basename -- "$1")"
簡易な(行数が少ない)コードを求めているのであれば上記のコードで良いのですがシェルスクリプトにするのであれば行数を気にする必要はないと思うのでシェル関数にしました。この方法のメリットは外部コマンドを使っておらずファイルやディレクトリが存在していなくても動作するので速いという点です。改行で終わるパスにも対応しています。POSIX 準拠で bash 以外にも対応しており(ローカル)変数の代わりに位置パラメータを使うことで、グローバル変数を汚染しないようにしています。
abspath() {
case $2 in
/*) set -- "$1" "$2/" "" ;;
*) set -- "$1" "${3:-$PWD}/$2/" ""
esac
while [ "$2" ]; do
case ${2%%/*} in
"" | .) set -- "$1" "${2#*/}" "$3" ;;
..) set -- "$1" "${2#*/}" "${3%/*}" ;;
*) set -- "$1" "${2#*/}" "$3/${2%%/*}"
esac
done
eval "$1=\"/\${3#/}\""
}
relpath() {
set -- "$1" "$2" "${3:-$PWD}"
abspath "$@"
eval "set -- \"\$1\" \"\${$1}\" \"\$3\""
abspath "$1" "$3"
eval "set -- \"\$1\" \"\$2\" \"\${$1}\" \"\""
[ _"$2" = _"$3" ] && eval "$1=./" && return 0
while [ "$3" ]; do
eval "$1=\$3 && [ ! \"\$2\" = \"\${2#\"\${$1}\"}\" ]" && break
set -- "$1" "$2" "${3%/*}" "../$4"
done
eval "$1=\$3/ && $1=\$4\${2#\"\${$1}\"}"
}
変換済みのパスは標準出力に出力されるのではなく第一引数に指定した変数名に返します。read
関数と同じような仕様です。これにより改行で終わるパスにも対応できます。第三引数を指定するとカレントディレクトリではなく指定したディレクトリを基準として変換します。
abspath ret "$1"
printf '[%s]\n' "$ret"
relpath ret "$1"
printf '[%s]\n' "$ret"
一応ローカルでテストはしてるのですが、過去の bash 用に作ったコードを関数のインターフェースを変更して POSIX 準拠に書き換えており使用実績もあまりないので利用する際は注意してください。
このコードのライセンスは CC0 とします。近いうちにこれらを含め私が作った or 作る予定のシェルスクリプト用関数を集めたリポジトリを作る予定です。
また関連記事としてこちらもどうぞ
更新履歴
- 2021-09-07
-
abspath()
関数の未使用の位置パラメータを削除しました - コードのライセンスを MIT ライセンスから CC0 に変更しました
-