Как узнать имя файла скрипта в скрипте Bash?

606

Как определить имя файла сценария Bash внутри самого сценария?

Например, если мой сценарий находится в файле runme.sh, то как мне сделать так, чтобы он отображал сообщение «Вы запускаете runme.sh» без жесткого кодирования?

Ma99uS
источник
3
Аналогично [Может ли bash-скрипт сказать, в каком каталоге он хранится?] ( To stackoverflow.com/questions/59895/… )
Родриг,
Информацию о каталогах смотрите в разделе: Получение исходного каталога скрипта Bash изнутри .
Кенорб

Ответы:

631
me=`basename "$0"`

Для чтения символической ссылки 1 , которая обычно не является тем, что вы хотите (обычно вы не хотите путать пользователя таким образом), попробуйте:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

ИМО, это даст непонятный результат. "Я запустил foo.sh, но он говорит, что я запускаю bar.sh !? Должно быть, это ошибка!" Кроме того, одной из целей использования символических ссылок с разными именами является предоставление различных функциональных возможностей в зависимости от имени, которое оно называется (например, gzip и gunzip на некоторых платформах).


1 То есть, чтобы разрешить символические foo.shссылки таким образом, чтобы при выполнении пользователем, который на самом деле является символической ссылкой bar.sh, вы хотите использовать разрешенное имя, bar.shа не foo.sh.

Tanktalus
источник
40
$ 0 дает вам имя, через которое был вызван скрипт, а не реальный путь к реальному файлу скрипта.
Крис Конвей
4
Это работает, если вас не вызывают по символической ссылке. Но даже тогда, как правило, это то, что вы хотите, IME.
Tanktalus
78
Не работает для сценариев, которые получены в отличие от вызванных.
Чарльз Даффи
1
@ Чарльз Даффи: См . Ответ Дмитрия Радулова ниже .
Серж Вотье
8
-1, 1. readlink будет перемещаться только на одну символическую ссылку на глубину, 2. $0в первом примере это разделение слов, 3. $0передается на basename, readlink и echo в позиции, которая позволяет рассматривать его как переключатель командной строки , Я предлагаю вместо этого me=$(basename -- "$0")или намного более эффективно за счет читабельности me=${0##*/}. Для символических ссылок, me=$(basename -- "$(readlink -f -- "$0")")предполагающих использование утилиты gnu, в противном случае это будет очень длинный скрипт, который я не буду здесь писать.
Score_Under
246
# ------------- СЦЕНАРИЙ ------------- # # ------------- ВЫЗОВ ------ ------- #

#!/bin/bash

echo
echo "# arguments called with ---->  ${@}     "
echo "# \$1 ---------------------->  $1       "
echo "# \$2 ---------------------->  $2       "
echo "# path to me --------------->  ${0}     "
echo "# parent path -------------->  ${0%/*}  "
echo "# my name ------------------>  ${0##*/} "
echo
exit



# Обратите внимание на следующую строку, первый аргумент вызывается в двойных, # и одинарных кавычках, так как он содержит два слова


$   / misc / shell_scripts / check_root / show_parms . sh "'здравствуй'" "'william'" 

# ------------- РЕЗУЛЬТАТЫ ------------- #

# аргументы вызываются с помощью ---> 'hello there' 'william' # $ 1 ----------------------> 'hello there' # $ 2 ---- ------------------> 'william' # путь ко мне --------------> / misc / shell_scripts / check_root / show_parms. sh # родительский путь -------------> / misc / shell_scripts / check_root # мое имя -----------------> show_parms.sh






# ------------- КОНЕЦ ------------- #
Билл Эрнандес
источник
11
@NickC см. Удаление подстроки
cychoi
1
Как мне получить просто show_params, то есть имя без какого-либо дополнительного расширения?
Acumenus
Не работает, если скрипт вызывается из другой папки. Путь включен в ${0##*/}. Протестировано с использованием GitBash.
Алик Эльзин-килака
1
Вышеприведенное не работает для меня из скрипта .bash_login, но решение Dimitre Radoulov в $ BASH_SOURCE прекрасно работает.
Джон
@ Джон Конечно, ты имеешь в виду .bash_profile ? Или альтернативно .bashrc ? Вы должны знать, однако, что есть некоторые тонкие различия в том, как они вызваны / получены. Я очень устала, но если я правильно подумаю, проблема, скорее всего, будет. Одно из мест, где это имеет значение, - это если вы входите в систему удаленно, например, через ssh или локальную систему.
Pryftan
193

С bash> = 3 работает следующее:

$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s

$ cat s
#!/bin/bash

printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"
Димитр Радулов
источник
24
Большой! Это ответ, который работает как для ./scrip.sh, так и для источника ./script.sh
zhaorufei 11.10.10
4
Это то, что я хочу, и легко использовать " dirname $BASE_SOURCE", чтобы получить каталог, в котором находятся скрипты.
Ларри Кай
2
Я почти усвоил эту разницу при написании сценария самоудаления. К счастью, «rm» получил псевдоним «rm -i» :)
kervin
в любом случае. ./s чтобы получить имя ./s вместо bash? Я обнаружил, что $ 1 не всегда установлен на ./s ...
Дэвид Мокон Бонд
1
Это в BASH_SOURCE , не так ли?
Димитр Радулов
69

$BASH_SOURCE дает правильный ответ при поиске сценария.

Это, однако, включает путь, чтобы получить только имя файла сценария, используйте:

$(basename $BASH_SOURCE) 
Заинька
источник
2
Этот ответ является ИМХО лучшим, потому что решение использует самодокументируемый код. $ BASH_SOURCE полностью понятен без чтения какой-либо документации, тогда как, например, $ {0 ## * /} нет
Andreas M. Oberheim
Этот ответ имеет большую ценность, я верю, потому что, если мы запустим как . <filename> [arguments], $0дадим имя вызывающей оболочки. Ну, по крайней мере, на OSX точно.
Михир
68

Если имя сценария содержит пробелы, более надежный способ заключается в использовании "$0"или "$(basename "$0")"- или на MacOS: "$(basename \"$0\")". Это предотвращает искажение или интерпретацию имени любым способом. Как правило, рекомендуется всегда заключать в кавычки имена переменных в оболочке.

Джош Ли
источник
2
+1 за прямой ответ + лаконично. если нужны функции символической ссылки, я предлагаю посмотреть: ответ Трэвиса Б. Хартвелла.
Тревор Бойд Смит
26

Если вы хотите это без пути, то вы бы использовали ${0##*/}

Мистер ондатра
источник
А что, если я хочу это без какого-либо дополнительного расширения файла?
Acumenus
Чтобы удалить расширение, вы можете попробовать «$ {VARIABLE% .ext}», где VARIABLE - это значение, которое вы получили от $ {0 ## * /}, а «.ext» - это расширение, которое вы хотите удалить.
BrianV
19

Чтобы ответить Крису Конвею , в Linux (по крайней мере) вы должны сделать это:

echo $(basename $(readlink -nf $0))

readlink выводит значение символической ссылки. Если это не символическая ссылка, печатается имя файла. -n говорит не печатать перевод строки. -f говорит, что следует полностью перейти по ссылке (если символическая ссылка была ссылкой на другую ссылку, она также разрешит эту ссылку).

Трэвис Б. Хартвелл
источник
2
-n безвреден, но не обязателен, потому что структура $ (...) его обрежет.
Жаоруфеи
16

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

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

Если вы хотите следовать символическим ссылкам, используйте их readlinkпо пути, указанному выше, рекурсивно или не рекурсивно.

Причина, по которой работает однострочник, объясняется использованием BASH_SOURCEпеременной среды и ее ассоциированной переменной FUNCNAME.

BASH_SOURCE

Переменная массива, членами которой являются имена файлов источника, где определены соответствующие имена функций оболочки в переменной массива FUNCNAME. Функция оболочки $ {FUNCNAME [$ i]} определена в файле $ {BASH_SOURCE [$ i]} и вызвана из $ {BASH_SOURCE [$ i + 1]}.

имя_функции

Переменная массива, содержащая имена всех функций оболочки, находящихся в данный момент в стеке вызовов выполнения. Элемент с индексом 0 - это имя любой выполняемой в настоящее время функции оболочки. Самый нижний элемент (с наивысшим индексом) является «основным». Эта переменная существует только при выполнении функции оболочки. Назначения для FUNCNAME не имеют никакого эффекта и возвращают статус ошибки. Если FUNCNAME не установлено, оно теряет свои специальные свойства, даже если оно впоследствии сбрасывается.

Эта переменная может использоваться с BASH_LINENO и BASH_SOURCE. Каждый элемент FUNCNAME имеет соответствующие элементы в BASH_LINENO и BASH_SOURCE для описания стека вызовов. Например, $ {FUNCNAME [$ i]} был вызван из файла $ {BASH_SOURCE [$ i + 1]} по номеру строки $ {BASH_LINENO [$ i]}. Встроенная функция вызывающего абонента отображает текущий стек вызовов, используя эту информацию.

[Источник: руководство Bash]

gkb0986
источник
2
Это работает, если вы исходите из файла a (предположим, aчто содержимое echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") из интерактивного сеанса - тогда он даст вам aпуть. Но если вы пишете сценарий bс source aв нем и работать ./b, он будет возвращать bпуть «s.
PSkocik
12

Эти ответы верны для тех случаев, в которых они указаны, но проблема остается, если вы запускаете сценарий из другого сценария, используя ключевое слово «source» (чтобы он выполнялся в той же оболочке). В этом случае вы получаете $ 0 вызывающего скрипта. И в этом случае я не думаю, что возможно получить название самого сценария.

Это крайний случай, и к нему не следует относиться серьезно. Если вы запустите скрипт из другого скрипта напрямую (без 'source'), будет работать $ 0.

Джим додд
источник
2
У тебя очень хорошая мысль. Не крайний случай ИМО. Однако есть решение: см . Ответ Дмитрия Радулова выше
Серж Вотье,
10

Поскольку некоторые комментарии спрашивают о имени файла без расширения, вот пример, как это сделать:

FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}

Наслаждайтесь!

Саймон Маттес
источник
9

Re: ответ Tanktalus (принятый) выше, более чистый способ заключается в использовании:

me=$(readlink --canonicalize --no-newline $0)

Если ваш скрипт был получен из другого скрипта bash, вы можете использовать:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

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

Саймон
источник
1
Спасибо за sourced имена сценариев.
Фредрик Гаусс
8

Вы можете использовать $ 0, чтобы определить имя вашего скрипта (с полным путем) - чтобы получить имя скрипта, вы можете только обрезать эту переменную с помощью

basename $0
Волька
источник
8

если ваш вызов сценария оболочки, как

/home/mike/runme.sh

$ 0 - полное имя

 /home/mike/runme.sh

Базовое имя $ 0 получит базовое имя файла

 runme.sh

и вам нужно поместить это основное имя в переменную, такую ​​как

filename=$(basename $0)

и добавьте свой дополнительный текст

echo "You are running $filename"

так что ваши сценарии как

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"
LawrenceLi
источник
7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

Это разрешает символические ссылки (это делает realpath), обрабатывает пробелы (это делают двойные кавычки) и находит текущее имя сценария, даже когда оно получено (. ./Myscript) или вызвано другими сценариями (это обрабатывает $ BASH_SOURCE). После всего этого полезно сохранить это в переменной среды для повторного использования или для простого копирования в другом месте (this =) ...

jcalfee314
источник
3
К вашему сведению, realpathэто не встроенная команда BASH. Это автономный исполняемый файл, который доступен только в определенных дистрибутивах
StvnW
4

В bashвы можете получить имя файла сценария с помощью $0. Как правило $1, и $2т. Д. Для доступа к аргументам CLI. Точно так же $0доступ к имени, которое запускает сценарий (имя файла сценария).

#!/bin/bash
echo "You are running $0"
...
...

Если вы вызываете скрипт с путем, как /path/to/script.shто, то $0также даст имя файла с путем. В этом случае нужно использовать, $(basename $0)чтобы получить только имя файла скрипта.

rashok
источник
3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
ecwpz91
источник
2

$0не отвечает на вопрос (насколько я понимаю). Демонстрация:

$ cat script.sh
#! / Бен / ш
echo `basename $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

Как можно ./linktoscriptраспечатать script.sh?

[EDIT] Согласно @ephemient в комментариях выше, хотя символическая ссылка может показаться надуманной, можно поиграться с тем $0, чтобы она не представляла ресурс файловой системы. ОП немного двусмысленно о том, что он хотел.

Крис Конвей
источник
добавил ответ на это к моему ответу выше, но я не согласен с тем, что это именно то, что действительно нужно (или что вы должны этого хотеть, за исключением некоторых смягчающих обстоятельств, которые я не могу сейчас понять)
Tanktalus
2
Я думаю, что печать linktoscript вместо script.sh - это особенность, а не ошибка. Несколько команд Unix используют свое имя для изменения своего поведения. Примером является vi / ex / view.
Mouviciel
@Tanktalus: Есть случаи, когда вы хотите, чтобы поведение изменялось в зависимости от реального местоположения файла - в предыдущем проекте у меня был сценарий «Активная ветвь», который вставлял в ссылку несколько путей из той ветки, в которой я находился. работа над; затем эти инструменты могли бы узнать, в какой каталог они были загружены для работы с нужными файлами.
Эндрю Айлетт
2

Информация благодаря Биллу Эрнандесу. Я добавил некоторые предпочтения, которые я принимаю.

#!/bin/bash
function Usage(){
    echo " Usage: show_parameters [ arg1 ][ arg2 ]"
}
[[ ${#2} -eq 0 ]] && Usage || {
    echo
    echo "# arguments called with ---->  ${@}     "
    echo "# \$1 ----------------------->  $1       "
    echo "# \$2 ----------------------->  $2       "
    echo "# path to me --------------->  ${0}     " | sed "s/$USER/\$USER/g"
    echo "# parent path -------------->  ${0%/*}  " | sed "s/$USER/\$USER/g"
    echo "# my name ------------------>  ${0##*/} "
    echo
}

ура

linxuser
источник
1

Коротко, ясно и просто, в my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

echo "You are running '$running_file_name' file."

Вывод:

./my_script.sh
You are running 'my_script.sh' file.
Bơ Loong A Nhứi
источник
0

Вот что я придумал, вдохновленный Dimitre Radoulov ответ «s (который я upvoted, кстати) .

script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"

echo "Called $script with $# argument(s)"

независимо от того, как вы называете свой сценарий

. path/to/script.sh

или

./path/to/script.sh
Salathiel Genèse
источник
-1

echo "Вы бежите $ 0"

mmacaulay
источник
Это не bash скрипт, это полный путь.
e-info128
-6

что-то подобное?

export LC_ALL=en_US.UTF-8
#!/bin/bash
#!/bin/sh

#----------------------------------------------------------------------
start_trash(){
ver="htrash.sh v0.0.4"
$TRASH_DIR  # url to trash $MY_USER
$TRASH_SIZE # Show Trash Folder Size

echo "Would you like to empty Trash  [y/n]?"
read ans
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
then
echo "'yes'"
cd $TRASH_DIR && $EMPTY_TRASH
fi
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
then
echo "'no'"
fi
 return $TRUE
} 
#-----------------------------------------------------------------------

start_help(){
echo "HELP COMMANDS-----------------------------"
echo "htest www                 open a homepage "
echo "htest trash               empty trash     "
 return $TRUE
} #end Help
#-----------------------------------------------#

homepage=""

return $TRUE
} #end cpdebtemp

# -Case start
# if no command line arg given
# set val to Unknown
if [ -z $1 ]
then
  val="*** Unknown  ***"
elif [ -n $1 ]
then
# otherwise make first arg as val
  val=$1
fi
# use case statement to make decision for rental
case $val in
   "trash") start_trash ;;
   "help") start_help ;;
   "www") firefox $homepage ;;
   *) echo "Sorry, I can not get a $val   for you!";;
esac
# Case stop
hynt
источник
5
-1, не отвечает на вопрос (не показывает, как найти имя скрипта) и является запутанным примером в очень глючном скрипте.
Score_Under