Skip to content

Instantly share code, notes, and snippets.

@miyaokamarina
Created September 23, 2020 09:27
Show Gist options
  • Save miyaokamarina/12873943962e1e580252889c3643486a to your computer and use it in GitHub Desktop.
Save miyaokamarina/12873943962e1e580252889c3643486a to your computer and use it in GitHub Desktop.
Node’s `path.normalize`*, but a POSIX Shell function.

* Unlike the original, this function removes trailind slashes, and keeps leading ./ segments.

#!/bin/sh
# Test functions
_normalize_path__is_abs() {
case "$_normalize_path__path" in
/*) true ;;
*) false ;;
esac
}
_normalize_path__res_ends_with_parent() {
case "$_normalize_path__res" in
.. | */..) true ;;
*) false ;;
esac
}
# List operations
_normalize_path__push() {
if [ "$_normalize_path__res" = "" ]; then
_normalize_path__res=$_normalize_path__seg
else
_normalize_path__res=$_normalize_path__res/$_normalize_path__seg
fi
}
_normalize_path__pop() {
case "$_normalize_path__res" in
*/*) _normalize_path__res=${_normalize_path__res%/*} ;;
.) _normalize_path__res=.. ;;
*) _normalize_path__res="" ;;
esac
}
# Almost compatible with Node’s `path.posix.normalize`,
# but unlike the original, removes trailing slashes, and keeps leading dots.
#
# For usage examples, see commented section at the end of file.
normalize_path() {
_normalize_path__path=${1-}
_normalize_path__src=$_normalize_path__path
_normalize_path__seg=""
_normalize_path__res=""
# Main loop
while [ "$_normalize_path__src" != "" ]; do
case "$_normalize_path__src" in
*/*)
_normalize_path__seg=${_normalize_path__src%%/*}
_normalize_path__src=${_normalize_path__src#*/}
;;
*)
_normalize_path__seg=$_normalize_path__src
_normalize_path__src=""
;;
esac
case "$_normalize_path__seg" in
"") true ;; # Empty segment. Noop.
.) if [ "$_normalize_path__res" = "" ]; then #
_normalize_path__push # Leading dot. Push.
fi ;; # Non-leading dot. Noop.
..) if [ "$_normalize_path__res" = "" ] && _normalize_path__is_abs; then #
true # At an absolute root. Noop.
elif [ "$_normalize_path__res" = "" ] || _normalize_path__res_ends_with_parent; then #
_normalize_path__push # Relative parent unknown. Push.
else #
_normalize_path__pop # Parent known. Pop.
fi ;; #
*) _normalize_path__push ;; # Regular regment. Push.
esac
done
# Fix absolute path.
if _normalize_path__is_abs; then
_normalize_path__res=/$_normalize_path__res
fi
echo "$_normalize_path__res"
}
# _normalize_path__test() {
# n="${1-}"
# s="${2-}"
# d="${3-}"
# e="${4-}"
# a="$(normalize_path "$s")"
# #
# if [ "$a" != "$e" ]; then
# echo "$n :: FAIL :: $s $d $a"
# else
# echo "$n :: OK :: $s $d $a"
# fi
# }
# #
# _normalize_path__test 00:lat 'a/./b/./c' -------------------------- 'a/b/c'
# _normalize_path__test 01:lat 'a/././b/././c' ---------------------- 'a/b/c'
# _normalize_path__test 02:lat 'a//b//c' ---------------------------- 'a/b/c'
# _normalize_path__test 03:lat 'a///b///c' -------------------------- 'a/b/c'
# _normalize_path__test 04:lat 'a/b/c/' ----------------------------- 'a/b/c'
# _normalize_path__test 05:lat 'a/b/c/.' ---------------------------- 'a/b/c'
# _normalize_path__test 06:lat 'a/./b/./c/./' ----------------------- 'a/b/c'
# _normalize_path__test 07:lat 'a/././b/././c/././' ----------------- 'a/b/c'
# _normalize_path__test 08:lat 'a//b//c//' -------------------------- 'a/b/c'
# _normalize_path__test 09:lat 'a///b///c///' ----------------------- 'a/b/c'
# _normalize_path__test 10:lat 'a/./b/./c/./.' ---------------------- 'a/b/c'
# _normalize_path__test 11:lat 'a/././b/././c/././.' ---------------- 'a/b/c'
# _normalize_path__test 12:lat 'a//b//c//.' ------------------------- 'a/b/c'
# _normalize_path__test 13:lat 'a///b///c///.' ---------------------- 'a/b/c'
# _normalize_path__test 14:lat '/a/b/c' ----------------------------- '/a/b/c'
# _normalize_path__test 15:lat '//a/b/c' ---------------------------- '/a/b/c'
# _normalize_path__test 16:lat '/../a/b/c' -------------------------- '/a/b/c'
# _normalize_path__test 17:lat '/../../a/b/c' ----------------------- '/a/b/c'
# _normalize_path__test 18:lat '/../_/../a/b/c' --------------------- '/a/b/c'
# _normalize_path__test 19:lat './a/b/c' ---------------------------- './a/b/c'
# _normalize_path__test 20:lat '../a/b/c' --------------------------- '../a/b/c'
# _normalize_path__test 21:lat '../../a/b/c' ------------------------ '../../a/b/c'
# _normalize_path__test 22:lat '_/../a/b/c' ------------------------- 'a/b/c'
# _normalize_path__test 23:lat '_/_/../../a/b/c' -------------------- 'a/b/c'
# _normalize_path__test 24:lat './_/../a/b/c' ----------------------- './a/b/c'
# _normalize_path__test 25:lat './_/_/../../a/b/c' ------------------ './a/b/c'
# _normalize_path__test 26:lat './_/_/../../../a/b/c' --------------- '../a/b/c'
# _normalize_path__test 27:lat 'a/_/_/../../b/_/_/_/../../../c' ----- 'a/b/c'
# _normalize_path__test 28:lat 'a/./.env*/./c' ---------------------- 'a/.env*/c'
# #
# _normalize_path__test 00:jap 'あ/./い/./う' -------------------------- 'あ/い/う'
# _normalize_path__test 01:jap 'あ/././い/././う' ---------------------- 'あ/い/う'
# _normalize_path__test 02:jap 'あ//い//う' ---------------------------- 'あ/い/う'
# _normalize_path__test 03:jap 'あ///い///う' -------------------------- 'あ/い/う'
# _normalize_path__test 04:jap 'あ/い/う/' ----------------------------- 'あ/い/う'
# _normalize_path__test 05:jap 'あ/い/う/.' ---------------------------- 'あ/い/う'
# _normalize_path__test 06:jap 'あ/./い/./う/./' ----------------------- 'あ/い/う'
# _normalize_path__test 07:jap 'あ/././い/././う/././' ----------------- 'あ/い/う'
# _normalize_path__test 08:jap 'あ//い//う//' -------------------------- 'あ/い/う'
# _normalize_path__test 09:jap 'あ///い///う///' ----------------------- 'あ/い/う'
# _normalize_path__test 10:jap 'あ/./い/./う/./.' ---------------------- 'あ/い/う'
# _normalize_path__test 11:jap 'あ/././い/././う/././.' ---------------- 'あ/い/う'
# _normalize_path__test 12:jap 'あ//い//う//.' ------------------------- 'あ/い/う'
# _normalize_path__test 13:jap 'あ///い///う///.' ---------------------- 'あ/い/う'
# _normalize_path__test 14:jap '/あ/い/う' ----------------------------- '/あ/い/う'
# _normalize_path__test 15:jap '//あ/い/う' ---------------------------- '/あ/い/う'
# _normalize_path__test 16:jap '/../あ/い/う' -------------------------- '/あ/い/う'
# _normalize_path__test 17:jap '/../../あ/い/う' ----------------------- '/あ/い/う'
# _normalize_path__test 18:jap '/../_/../あ/い/う' --------------------- '/あ/い/う'
# _normalize_path__test 19:jap './あ/い/う' ---------------------------- './あ/い/う'
# _normalize_path__test 20:jap '../あ/い/う' --------------------------- '../あ/い/う'
# _normalize_path__test 21:jap '../../あ/い/う' ------------------------ '../../あ/い/う'
# _normalize_path__test 22:jap '_/../あ/い/う' ------------------------- 'あ/い/う'
# _normalize_path__test 23:jap '_/_/../../あ/い/う' -------------------- 'あ/い/う'
# _normalize_path__test 24:jap './_/../あ/い/う' ----------------------- './あ/い/う'
# _normalize_path__test 25:jap './_/_/../../あ/い/う' ------------------ './あ/い/う'
# _normalize_path__test 26:jap './_/_/../../../あ/い/う' --------------- '../あ/い/う'
# _normalize_path__test 27:jap 'あ/_/_/../../い/_/_/_/../../../う' ----- 'あ/い/う'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment