При условии, что у вас есть разрешения на выполнение в текущем каталоге - или в каталоге, из которого вы выполняли свой сценарий оболочки, - если вам нужен абсолютный путь к каталогу, все, что вам нужно cd
.
Шаг 10 из cd
спецификации
Если -P
опция действует, $PWD
переменная окружения должна быть установлена в строку, которая будет выведена pwd -P
. Если для нового текущего каталога или любого родительского элемента этого каталога недостаточно прав для определения текущего рабочего каталога, значение $PWD
переменной среды не указывается.
И на pwd -P
Путь, записанный в стандартный вывод, не должен содержать никаких компонентов, которые ссылаются на файлы типа символьной ссылки. Если есть несколько путей, которые pwd
утилита может записать в стандартный вывод, одно из которых начинается с одного символа / косой черты, а другое или более, начиная с двух символов / косой черты, то она должна записать имя пути, начинающееся с одного символа / косой черты. Путь не должен содержать никаких ненужных символов / косой черты после первого или двух символов косой черты.
Это потому, что cd -P
должен установить текущий рабочий каталог на то, что pwd -P
должно печатать иначе, и это cd -
должно напечатать, $OLDPWD
что работает следующее:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
ВЫХОД
/home/mikeserv/test/ln
ждать его...
cd -P . ; cd . ; cd -
ВЫХОД
/home/mikeserv/test/dir
И когда я печатаю с cd -
я печатаю $OLDPWD
. cd
устанавливает, $PWD
как только я cd -P .
$PWD
теперь абсолютный путь к /
- поэтому мне не нужны никакие другие переменные. И на самом деле, я даже не нужна буксировка , .
но есть указанное поведение сброса $PWD
в $HOME
в интерактивной оболочке , когда cd
это прикрасы. Так что это просто хорошая привычка развиваться.
Так что простого выполнения вышеупомянутого пути ${0%/*}
должно быть более чем достаточно, чтобы проверить $0
путь, но в случае, если $0
это само по себе мягкая ссылка, вы, к сожалению, не можете изменить каталог на него.
Вот функция, которая справится с этим:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
Он стремится сделать столько, сколько мог бы в текущей оболочке - не вызывая подоболочку - хотя существуют подоболочки, вызываемые для ошибок и программных ссылок, которые не указывают на каталоги. Это зависит от POSIX-совместимой оболочки и POSIX-совместимой, ls
а также от чистого _function()
пространства имен. Без последнего он будет работать нормально, хотя в этом случае он может перезаписать unset
некоторые текущие функции оболочки. В общем, все эти зависимости должны быть достаточно надежно доступны на Unix-машине.
Вызывается с аргументами или без них, и первое, что он делает, - возвращается $PWD
к своему каноническому значению - при необходимости он разрешает любые ссылки в них на свои цели. Называется без аргументов и все тут; но вызывается с ними, и он будет разрешать и канонизировать путь для каждого или распечатать сообщение, stderr
почему бы и нет.
Поскольку он в основном работает в текущей оболочке, он должен иметь возможность обрабатывать список аргументов любой длины. Он также ищет $_zdlm
переменную (которую онunset
также просматривает, когда проходит через нее) и печатает ее C-escape-значение сразу справа от каждого из своих аргументов, за каждым из которых всегда следует также один \n
символ ewline.
Он сильно меняет каталог, но, кроме установки его канонического значения, он не влияет $PWD
, хотя $OLDPWD
на него нельзя рассчитывать, когда он закончится.
Он пытается выйти из каждого из своих аргументов как можно скорее. Сначала он пытается cd
в $1
. Если это возможно, он печатает канонический путь аргумента к stdout
. Если он не может, он проверяет, что $1
существует и не является мягкой ссылкой. Если это правда, он печатает.
Таким образом, он обрабатывает любой аргумент типа файла, на который оболочка имеет права доступа, если только $1
это не символическая ссылка, которая не указывает на каталог. В этом случае он вызывает while
цикл в подоболочке.
Звонит, ls
чтобы прочитать ссылку. Текущий каталог должен быть сначала изменен на свое первоначальное значение, чтобы надежно обрабатывать любые референтные пути, и поэтому в подобласти подстановки команд функция делает:
cd -...ls...echo /
Он отбрасывает слева от ls
выходных данных столько, сколько должен полностью содержать имя ссылки и строку ->
. Хотя я сначала пытался избежать этого, shift
и $IFS
оказалось, что это самый надежный метод, насколько я могу судить. Это то же самое, что делает Жиль_Плохая_мани_челка - и это хорошо сделано.
Он будет повторять этот процесс в цикле до тех пор, пока возвращаемое имя файла не ls
станет мягкой ссылкой. В этот момент он канонизирует этот путь, как и раньше, с cd
последующей печатью.
Пример использования:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
ВЫХОД
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
Или возможно ...
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
ВЫХОД
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...