При условии, что у вас есть разрешения на выполнение в текущем каталоге - или в каталоге, из которого вы выполняли свой сценарий оболочки, - если вам нужен абсолютный путь к каталогу, все, что вам нужно 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' -> ...