LoginSignup
3
3

More than 1 year has passed since last update.

シェルスクリプトで相対パスと絶対パスを相互に変換する関数

Last updated at Posted at 2020-09-28

こちら(シェルスクリプトで相対パスを絶対パスに変換する正しい方法)の記事の続きです。

相対パスを絶対パスに変換する方法

# 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 に変更しました
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3