Какой платформенный независимый способ найти каталог оболочки, исполняемый в оболочке script?

В соответствии с POSIX:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html

есть случаи, когда это не очевидно. Например:

If the file is not in the current working directory,
the implementation may perform a search for an executable
file using the value of PATH, as described in Command Search and Execution.

My Bash 4.x не следует этому необязательному правилу (из-за проблем с безопасностью?), поэтому я не могу проверить, как это происходит в реальной жизни...

Какой платформенный независимый способ найти каталог исполняемого файла оболочки в оболочке script?

PS. Также сбой dirname $0:

#!/bin/sh
echo $0
dirname $0

когда вы:

$ sh runme.sh
runme.sh
.

Итак, вам нужно что-то вроде:

CMDPATH=`cd $(dirname $0); echo $PWD`

Чтобы код зависел только от встроенных возможностей оболочки, я переписываю код:

PREVPWD=$PWD
cd ${0%${0##*/}}.
CMDPATH=$PWD
cd $PREVPWD

Это выглядит уродливым, но не требует fork любых исполняемых файлов...

Ответ 1

EDIT3:

Хотя не строго POSIX, realpath является основным приложением GNU с 2012 года. Полное раскрытие: никогда не слышал об этом, прежде чем я заметил его в TOC info coreutils и сразу подумал об этом вопросе, но используя следующую функцию, как показано, должен быть надежно (скоро POSIXLY?) И, надеюсь, эффективно предоставить своему вызывающему абоненту абсолютно исходный $0:

% _abs_0() { 
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath -s "${1}"`}; eval "$1=\${o1}"; 
> }  
% _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
/no/more/dots/in/your/path2.sh

EDIT4: Возможно, стоит подчеркнуть, что это решение использует расширение параметра POSIX, чтобы сначала проверить, на самом деле требуется расширение и разрешение вообще, прежде чем пытаться это сделать. Это должно вернуть абсолютно sourced $0 через переменную messenger (с заметным исключением, что -s будет сохранять symlinks) как эффективно, как я мог себе представить, что это может сделать , является ли путь уже абсолютным.

EDIT2:

Теперь я считаю, что я понимаю вашу проблему намного лучше, что, к сожалению, на самом деле делает большую часть ниже несущественных.

(незначительное редактирование): прежде чем найти realpath в документах, я по крайней мере позаботился о том, чтобы моя версия не зависела от поля времени, но, честное предупреждение, после тестирования некоторых я "менее убежденный ps полностью надежен в своей способности расширения пропускной способности)

С другой стороны, вы можете сделать это:

ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"

eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

Мне нужно исправить это, чтобы лучше работать с полями, вместо того, чтобы ожидать, что поле времени будет находиться непосредственно перед трактом процесса и полагаться на его включенный двоеточие в качестве ссылки, особенно потому, что это не будет работать с двоеточием на пути к процессу, но это тривиально и скоро произойдет, я думаю. Я полагаю, что функциональность совместима с POSIX. Вероятно, только расширение параметра может сделать то, что необходимо, я думаю.

Не строго релевантно (или правильно):

Это должно работать в каждом случае, соответствующем рекомендациям POSIX:

echo ${0%/*}

EDIT:

Поэтому я признаюсь, что, по крайней мере, сначала краснеть, я не совсем понимаю проблему, которую вы описываете. Очевидно, что в вашем вопросе вы демонстрируете некоторое знакомство с стандартами POSIX для манипуляций с переменными строками посредством расширения параметров (даже если ваша конкретная реализация кажется слегка напряженной с первого взгляда), поэтому, вероятно, я пропускаю важную информацию в своей интерпретации вашего вопроса и, возможно, по крайней мере в его нынешнем виде, это не тот ответ, который вы ищете.

Я опубликовал ранее о расширении параметра для встроенных переменных null/set тестов, которые могут или не могут быть полезными для вас, как вы можете видеть на "Портативном способе проверки пустоты переменная Shell Variable". Я упоминаю это главным образом потому, что мой ответ был в значительной степени скопирован/вставлен из руководств POSIX по расширению параметров, включает привязанная ссылка к рекомендациям охват по этому вопросу, а также несколько примеров как из канонической документации, так и из моих собственных, возможно, менее прочных конструкций.

Тем не менее, я свободно соглашусь с тем, что, хотя я еще не полностью понимаю, о чем вы спрашиваете, я не верю, что вы найдете там конкретный ответ. Вместо этого я подозреваю, что вы, возможно, иногда забывали, что операторы # и % в строчной манипуляции POSIX используются для указания части строки, которую вы хотите удалить, а не того, что вы хотите сохранить как можно скорее находят более интуитивно понятным. Я имею в виду, что любой фрагмент строки, который вы ищете таким образом, предназначен для того, чтобы исчезнуть из вашего вывода, а это будет только то, что осталось от вашей исходной строки после удаления указанной строки поиска.

Итак, вот немного обзора:

В то время как один экземпляр любого оператора будет удалять только как можно меньше, чтобы полностью удовлетворить ваш поиск, но когда дважды установлен поиск вызывается в жадной форме и удаляет как можно больше исходной строки , поскольку ваш поиск может позволить.

Кроме этого вам нужно знать только некоторое базовое регулярное выражение и помнить, что # начинает его поиск строки удаления слева и сканирует вправо, и что % начинает вместо этого поиск справаи сканирует влево.

## short example before better learning if I'm on the right track
## demonstrating path manipulation with '#' and '%'
% _path_one='/one/two/three/four.five'
% _path_two='./four.five'
## short searching from the right with our wildcard to the right 
## side of a single character removes everything to the right of 
## of the specified character and the character itself
## this is a very simple means of stripping extensions and paths
% echo ${_path_one%.*} ${_path_one%/*}
/one/two/three/four   /one/two/three
## long searching from the left with the wildcard to the left of
## course produces opposite results 
% echo ${_path_one##*.} ${_path_one##*/}
five    four.five

## will soon come back to show more probably
## short example before better learning if I'm on the right track
## demonstrating path manipulation with '#' and '%'
% _path_one='/one/two/three/four.five'
% _path_two='./four.five'
## short searching from the right with our wildcard to the right 
## side of a single character removes everything to the right of 
## of the specified character and the character itself
## this is a very simple means of stripping extensions and paths
% echo ${_path_one%.*} ${_path_one%/*}
/one/two/three/four   /one/two/three
## long searching from the left with the wildcard to the left of
## course produces opposite results 
% echo ${_path_one##*.} ${_path_one##*/}
five    four.five

## will soon come back to show more probably

Ответ 2

Я считаю, что вы можете получить его с помощью readlink:

scriptPath=$(readlink -f -- "$0")
scriptDirectory=${scriptPath%/*}