/bin/sh realpath++

Kamikaze

Warrior of Sunlight
Teammitglied
Für einen Tinderbox patch an dem ich arbeite brauchte ich ein Skript, das mir für irgend einen Pfad einen absoluten Pfad zurückgibt. Knackpunkt ist hier, das der Pfad nicht zwangsweise schon existieren muss. Das Skript muss den Pfad dann soweit wie möglich verfolgen und den Rest der noch nicht existiert beibehalten. Einer der Knackpunkte waren da Symlinks auf nicht existierende Pfade.

Mein Lösungsansatz (erst mal als separates Skript zum Testen) sieht so aus:
Code:
#!/bin/sh -f

follow() {
	local path tail link
	# Keep the portion to be resolved
	path=$1
	# The tail of the path that has been confirmed unresolvable
	tail=
	while [ -n "${path}" -a  ! -d "${path}" ]; do
		if [ -L "${path}" ]; then
			# Apparently this is a symlink, where the target
			# doesn't exist, resolve it
			link="$(readlink "${path}")"
			case $link in
			/*)
				path=${link}
				;;
			*)
				path=${path%/}
				path=${path%$(basename ${path})}${link}
				;;
			esac
			continue
		fi
		tail=${path##*/}${tail}
		path=${path%$(basename ${path})}
		tail=${path:+/}${tail}
		path=${path%/}
	done
	# Preserve / at end that will be stripped by realpath
	if [ -z "${tail}" -a "${path}" != "/" ]; then
		tail=${path%/}
		tail=${path#${tail}}
	fi
	echo "${path:+$(realpath ${path})}${tail}"
}

for arg in "$@"; do
	follow "$arg"
done
 
Zuletzt bearbeitet:
Sowas?

Code:
#!/bin/sh

pathname=$1
origpathname=${pathname}

while ! realpath -q ${pathname} >/dev/null; do
        pathname=$(dirname ${pathname})
done

echo $(realpath ${pathname})${origpathname#${pathname}}
 
> readlink bar
/home/kamikaze/foo/bar
> realpath++ bar
/usr/home/kamikaze/foo/bar

Deine Lösung:
> realpath+++ bar
/usr/home/kamikazebar
 
Ja, um ehrlich zu sein bin ich froh, dass das nicht klappt. Das hätte mich doch arg gewurmt, wenn es so einfach geht.
 
Neue Variante, kommt komplett ohne realpath aus. Das Aufräumen von ../, ./ usw. wird per sed erledigt. Der Vorteil ist, dass dann auch nicht auflösbare Teil des Pfads aufgeräumt wird.
Code:
#!/bin/sh -f

#
# This finds symlinks in a path and resolves them.
#
# It always outputs an absolute path and cleans up any ./ and ../ path
# components.
#
# Unlike realpath non-existant path components don't confuse this script.
#
# @param 1
#	The path to resolve
#
follow() {
	local path tail link
	path=$1
	tail=
	# Always deal with absolute paths
	if [ "${path%${path#?}}" != "/" ]; then
		path=${PWD}/${path}
	fi
	while [ -n "${path}" ]; do
		# Cut off tail
		if [ ! -L "${path}" ]; then
			tail=/${path##*/}${tail}
			path=${path%${path##*/}}
			path=${path%/}
			continue
		fi
		# Deal with symlinks
		# Note that path never ends with / here
		link=$(readlink "${path}")
		case $link in
		/*)
			# Absolute link
			path=${link}
			;;
		*)
			# Relative link
			path=${path%${path##*/}}${link}
			;;
		esac
	done
	# The substitions do:
	#	- Remove double /
	#	- Remove ./
	#	- Remove ^/..
	#	- Remove foo/..
	echo ${tail} \
		| sed -E -e :s \
		         -e 's,//,/,' \
		         -e 's,/\.(/|$),\1,' \
		         -e 's,^/..(/|$),\1,' \
		         -e 't s' \
		         -e 's,[^/]+/\.\.(/|$),\1,' \
		         -e 't s'
}

for arg in "$@"; do
	follow "$arg"
done
 
Zuletzt bearbeitet:
Zurück
Oben