Как заставить работать скрипт, использующий $0, если он находится в $PATH?

Как заставить работать скрипт, использующий $0, если он находится в $PATH?

У меня есть скрипт (~/.../gremlin), для которого я создал мягкую ссылку:

ln -s ~/[...]/gremlin ~/sc/gremlin

~/sc/gremlinнаходится в $PATH.
Вызов скрипта напрямую работает.
Вызов через ссылку — нет.
Он использует dirname $0, поэтому ничего не получает.

Есть ли способ заставить эту настройку работать, не меняя скрипт и не создавая еще один вместо программной ссылки?

Вот как используется $0:

case `uname` in
  CYGWIN*)
    CP=$( echo `dirname $0`/../lib/*.jar . | sed 's/ /;/g')
    ;;
  *)
    CP=$( echo `dirname $0`/../lib/*.jar . | sed 's/ /:/g')
esac

решение1

Является ли использование dirname $0проблемой или нет, будет зависеть откакон используется. Без дополнительных подробностей мы не можем ответить на этот вопрос.

Если (а) использование dirname $0вызывает проблемы и (б) вы не можете изменить скрипт, то решением будет заменить мягкую ссылку ~/sc/gremlinскриптом:

#!/bin/sh
exec ~/../gremlin "$@"

Таким образом, ~/../gremlinбудет работать с правильным $0.

Использование execозначает, что процесс, выполняющий скрипт, ~/../gremlinзаменит процесс, выполняющий ~/sc/gremlin. Это более эффективно, чем сохранение обоих процессов до ~/../gremlinзавершения.

решение2

Если вы хотите получить ~/...каталог из $0, а не ~/scиз скрипта, вы можете сделать следующее:

Если это zshсценарий:

#! /bin/zsh -
dir=$0:A:h

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

#! /bin/sh -
dir=$(dirname -- "$(readlink -f -- "$0")")

readlink -fи $0:Aдать вамканоническийпуть к файлу, то есть абсолютный путь после разрешения всех символических ссылок во всех компонентах пути.

А потом:

set -- "$dir"/../lib/*.jar .
case $(uname) in
  CYGWIN*) IFS=';';;
  *)       IFS=:;;
esac
CP="$*"

Или:

case $(uname) in
  CYGWIN*) sep=';';;
  *)       sep=:;;
esac
CP=$(printf "%s$sep" "$dir"/../lib/*.jar).

решение3

Как отметил @john1024, опечатка в вопросе автора может быть сутью вопроса:

ln -s ~/.../gremlin ~/sc/gremlin

Хотя некоторые приложения учитывают тройные точки, файловые системы Unix этого не делают. Вашоболочкарасширяет ~(тильду), а не ln, поэтому

    ln -s ~/../gremlin ~/sc/gremlin

расширит имя пути, как и ожидалось. Если ссылка уже существует, вы можете перезаписать ее с помощью параметра -f. Однако, если целью является реальный файл, -fпараметр молча перезапишет файл символической ссылкой. Вы можете захотеть использовать параметр -i, который, по крайней мере, скажет вам, что там уже что-то есть.

Если вы хотите проверить ссылку (доказать, что она указывает на файл), полезна -Lопция :ls

ls -lL ~/sc/gremlin

Если это не файл, lsто (в зависимости от реализации) будет отображаться только символ lдля типа (первый символ в списке) с вопросительными знаками для битов разрешений) или какое-либо другое указание на то, что он не указывает на реальный файл (OSX просто отказывается что-либо показывать в этом случае, хотя и учитывает размер символической ссылки в общем).

Дальнейшее чтение:

Связанный контент