shell - How to recursively resolve symlinks without readlink or realpath? -
what's best portable (posix?) way script finding target link if readlink
, realpath
not available?
would ls -l
, , if starts l
take text after ->
sed
, repeat until no longer starts l
?
per bashfaq #29 (which endorses gnu find approach suggested @eugeniurosca):
one available (though not pure-posix) option use perl
:
target=/path/to/symlink-name perl -le 'print readlink $env{target}'
if symlink's name guaranteed not contain ->
, can parse output of ls
.
the below code combines both approaches:
# define best readlink function available platform if command -v readlink >/dev/null 2>/dev/null; # first choice: use real readlink command readlink() { command readlink -- "$@" } elif find . -maxdepth 0 -printf '%l' >/dev/null 2>/dev/null; # second choice: use gnu find readlink() { local ll candidate >/dev/null 2>&1 ||: if candidate=$(find "$1" -maxdepth 0 -printf '%l') && [ "$candidate" ]; printf '%s\n' "$candidate" else printf '%s\n' "$1" fi } elif command -v perl >/dev/null 2>/dev/null; # third choice: use perl readlink() { local candidate ||: candidate=$(target=$1 perl -le 'print readlink $env{target}') if [ "$candidate" ]; printf '%s\n' "$candidate" else printf '%s\n' "$1" fi } else # fourth choice: parse ls -ld readlink() { local ll candidate >/dev/null 2>&1 ||: ll=$(lc_all=c ls -ld -- "$1" 2>/dev/null) candidate=${ll#* -> } if [ "$candidate" = "$ll" ]; printf '%s\n' "$1" else printf '%s\n' "$candidate" fi } fi readlink_recursive() { local path prev_path oldwd found_recursion >/dev/null 2>&1 ||: oldwd=$pwd; path=$1; found_recursion=0 while [ -l "$path" ] && [ "$found_recursion" = 0 ]; if [ "$path" != "${path%/*}" ]; cd -- "${path%/*}" || { cd -- "$oldwd" ||: echo "error: directory '${path%/*}' not exist in '$pwd'" >&2 return 1 } path=${pwd}/${path##*/} fi path=$(readlink "$path") if [ -d "$path" ]; cd -- "$path" path=$pwd break fi if [ "$path" != "${path%/*}" ]; cd -- "${path%/*}" || { echo "error: not traverse $pwd ${path%/*}" >&2 return 1 } path=${pwd}/${path##*/} elif [ "$pwd" != "$oldwd" ]; path=${pwd}/$path fi prev_path; if [ "$path" = "$prev_path" ]; found_recursion=1 break fi done set -- "$path" "$@" # record path recursion check done if [ "$path" != "${path%/../*}" ]; cd "${path%/*}" || { echo "error: directory '${path%/*}' not exist in $pwd" >&2 return 1 } printf '%s\n' "$pwd/${path##*/}" else printf '%s\n' "$path" fi cd -- "$oldwd" ||: }
Comments
Post a Comment