Как разделить переменную $ 0, чтобы найти каталог и относительные пути в bash?

10

Переменная $ 0 содержит информацию о пути скрипта.

  • Как я могу изменить информацию о пути к абсолютному пути? Я имею в виду, как обрабатывать ~,., .. или подобное?
  • Как я могу разбить информацию о пути на каталог и имя файла?

Я мог бы использовать python / perl для этого, но я хочу использовать bash, если это возможно.

prosseek
источник
что ты пытаешься достичь? Кстати. вызов сценария с помощью ~ / foo.sh приведет к расширению ~ в мой домашний каталог за $ 0 с моей версией bash (GNU bash, версия 4.1.5 (2) -release- (x86_64-unknown-linux-gnu))
echox

Ответы:

17

Вам не нужно обрабатывать такие вещи, как ~оболочка делает это за вас. Вот почему вы можете перейти ~/filenameк любому сценарию или программе, и это работает - все эти программы не обрабатывают ~себя, ваша оболочка преобразует аргумент /home/username/filenameи передает его программе:

$ echo ~/filename
/home/mrozekma/filename

Если вам нужно каноническое имя файла (которое не включает в себя такие вещи, как ..), используйте realpath(спасибо Нил ):

$ realpath ~/../filename
/home/filename

Что касается разделения пути на имя каталога и имя файла, используйте dirnameи basename:

$ dirname /foo/bar/baz
/foo/bar

$ basename /foo/bar/baz
baz
Михаил Мрозек
источник
Я думаю, что вы имеете в виду realpath, а не readlink. readlink не будет работать в обычной ситуации, то есть когда ссылка использует относительный путь, а вы не в том же каталоге, что и ссылка.
Нил Мэйхью
@Neil realpathлучше, но, readlinkкажется, работает, когда я пробую это; Вы знаете пример, где это терпит неудачу?
Майкл Мрозек
1
cd /tmp; touch foo; ln -s foo bar; cd; readlink /tmp/bar, Это возвращается fooи realpathвозвращается /tmp/foo, что вы хотите.
Нил Мэйхью
1
@Neil, @Michael: readlink -f(примечание -f) почти эквивалентен realpathи более переносим: readlink -fв GNU coreutils и существует также в нескольких других системах; realpathупакован отдельно и может быть не установлен (например, только 5 нестандартных пакетов зависят от него в Ubuntu 10.04 или Debian lenny).
Жиль "ТАК - перестань быть злым"
@ Жиль: хорошая мысль. Спасибо за напоминание о realpath -f.
Нил Мэйхью
5

Использование dirname и basename, как было упомянуто Майклом, должно быть самым безопасным способом получить то, что вы хотите.

В любом случае, если вы действительно хотите сделать это с помощью инструментов «bash only», вы можете использовать подстановку параметров:

echo `basename $PWD`        # Basename of current working directory.
echo "${PWD##*/}"           # Basename of current working directory.
echo
echo `basename $0`          # Name of script.
echo $0                     # Name of script.
echo "${0##*/}"             # Name of script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension of filename.

Этот пример взят из Руководства по расширенному написанию сценариев, которое стоит посмотреть.

Объяснение довольно простое:

$ {var # Pattern} Удалите из $ var самую короткую часть $ Pattern, соответствующую внешнему концу $ var. $ {var ## Pattern} Удалите из $ var самую длинную часть $ Pattern, соответствующую внешнему концу $ var.

Посмотрите на шаблон как на регулярное выражение #или ##как на жадный / не жадный модификатор.

Это может стать полезным, если вам придется делать более сложные извлечения части пути.

echox
источник
5
Остерегайтесь при использовании ${...##...}и друзей , а не dirnameи , basenameчто они не работают во всех случаях. Например, оба ${0##*/}и ${0%/*}расширить до того же, как $0будто $0не содержит /.
Жиль "ТАК - перестань быть злым"
@ Жиль, я понимаю, почему ${0%/*}это не хорошая альтернатива dirname. Однако, что не так с использованием ${0##*/}вместо basename?
Токсалот
@toxalot Для этого единственная проблема - это когда $0имя каталога с завершающим /. Но я сожалею о своем совете, потому dirnameчто не делает лучше.
Жиль "ТАК - перестань быть злым"
2

realpath это команда, которая говорит вам реальный путь (удаляет .. и символические ссылки и т. д.)

Это стандартно с FreeBSD. Согласно этому обсуждению это также доступно для Linux:

http://www.unix.com/shell-programming-scripting/89294-geting-real-path.html

Это обсуждение также предлагает решение Bash:

$ bash -c "cd /foo/../bar/ ; pwd"

источник
1
Именно так обычно и работает: MYDIR = "$ (cd $ (dirname" $ ​​0 "); pwd)"
Кевин Канту,