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

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

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

Подобно [Может ли сценарий bash сказать, в каком каталоге он хранится?] (К stackoverflow.com/questions/59895/…)

Rodrigue 05.07.2012 12:22

Для справочника см .: Получение исходного каталога сценария Bash изнутри.

kenorb 19.07.2017 21:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
657
2
483 650
23
Перейти к ответу Данный вопрос помечен как решенный

Ответы 23

Ответ принят как подходящий

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.

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

Chris Conway 10.10.2008 21:48

Это работает, если вас не вызывают по символической ссылке. Но даже в этом случае это обычно то, что вам нужно, IME.

Tanktalus 10.10.2008 21:50

Вы можете быть вызваны по имени, которое вообще не существует в виде файла - наиболее распространенный пример: вызов sh как "- / bin / sh" заставляет его действовать как оболочку входа в систему. См. exec -a в Bash или execvp("/bin/foo", {"blah", ...}).

ephemient 10.10.2008 22:48

Да, это лучший пример.

Chris Conway 11.10.2008 01:06

Если ваш скрипт изменяет свое поведение в зависимости от того, как он вызывается, неплохо было бы гарантировать, что при вычислении $ 0 какой-нибудь уловит все ... в противном случае простая символическая ссылка может заставить его делать странные вещи :) Т.е. если ссылка была названа foobar, а сценарий учитывает только foo или bar.

Tim Post 12.03.2009 20:03

Не работает для сценариев, которые исходят, а не вызываются.

Charles Duffy 27.01.2011 21:42

@ Чарльз Даффи: см. Ответ Димитра Радулова ниже.

Serge Wautier 27.10.2011 12:14

-1, 1. readlink будет перемещаться только на одну символическую ссылку в глубину, 2. $0 в первом примере подвержен разделению на слова, 3. $0 передается в basename, readlink и echo в позиции, которая позволяет рассматривать его как команду линейный переключатель. Я предлагаю вместо этого me=$(basename -- "$0") или, что гораздо эффективнее за счет читабельности, me=${0##*/}. Для символических ссылок me=$(basename -- "$(readlink -f -- "$0")") предполагает использование gnu utils, иначе это будет очень длинный сценарий, который я не буду здесь писать.

Score_Under 28.04.2015 20:22

echo "У вас $ 0"

Это не сценарий bash, это полный путь.

e-info128 23.05.2016 04:45

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

basename $0

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

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

Trevor Boyd Smith 28.08.2012 02:37

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

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

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

-n безвреден, но не обязателен, потому что структура $ (...) обрежет его.

zhaorufei 11.10.2010 11:06

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

А что, если я хочу его без дополнительного расширения файла?

Acumenus 08.07.2015 04:56

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

BrianV 19.04.2019 18:31

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

Это крайний случай, и его не следует воспринимать СЛИШКОМ серьезно. Если вы запустите сценарий напрямую из другого сценария (без «источника»), использование $ 0 будет работать.

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

Serge Wautier 27.10.2011 12:12

С 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"

Большой! Это ответ, который работает как для ./scrip.sh, так и для исходного ./script.sh

zhaorufei 11.10.2010 11:08

Это то, что я хочу, и легко использовать "dirname $BASE_SOURCE", чтобы получить каталог, в котором находятся скрипты.

Larry Cai 06.09.2011 08:43

Я почти осознал эту разницу на собственном горьком опыте, когда писал самоудаляющийся скрипт. К счастью, у 'rm' был псевдоним 'rm -i' :)

kervin 05.05.2012 01:47

в любом случае в. ./s, чтобы получить имя ./s вместо bash? Я обнаружил, что $ 1 не всегда устанавливается на ./s ...

David Mokon Bond 12.02.2013 18:14

Это в BASH_SOURCE, не так ли?

Dimitre Radoulov 12.02.2013 19:10

BASH_SOURCE - это массив, и его нельзя использовать таким образом.

styrofoam fly 04.07.2018 21:11

@kervin Осторожно с этим ... Это действительно похоже на присмотр за детьми, а что произойдет, если вы забудете настроить его в системе, в которой его нет по умолчанию? Я лично отключил его на всех своих системах. Это чертовски неприятно, и другой (один раз интерактивный) не намного лучше для меня. Кроме того, если я говорю, что хочу что-то удалить, я не хочу, чтобы меня спрашивали, уверен ли я в этом. Конечно, я в этом уверен! И даже если я введу неверный путь из-за опечатки, скажите, что есть такая вещь, как «резервное копирование». В любом случае, дело в том, что есть вероятность, что это не во всех системах и может быть катастрофическим.

Pryftan 22.07.2019 22:46

@Pryftan тогда какие альтернативы?

DarkTrick 12.10.2020 01:50

# ------------- SCRIPT ------------- #

#!/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

# ------------- CALLED ------------- #

# Notice on the next line, the first argument is called within double, 
# and single quotes, since it contains two words

$  /misc/shell_scripts/check_root/show_parms.sh "'hello there'" "'william'"

# ------------- RESULTS ------------- #

# arguments called with --->  'hello there' 'william'
# $1 ---------------------->  'hello there'
# $2 ---------------------->  'william'
# path to me -------------->  /misc/shell_scripts/check_root/show_parms.sh
# parent path ------------->  /misc/shell_scripts/check_root
# my name ----------------->  show_parms.sh

# ------------- END ------------- #

@NickC см. Удаление подстроки

cychoi 22.04.2014 21:00

Как мне получить только show_params, то есть имя без дополнительного расширения?

Acumenus 08.07.2015 04:54

Не работает, если скрипт вызывается из другой папки. Путь включен в ${0##*/}. Протестировано с использованием GitBash.

AlikElzin-kilaka 30.07.2015 09:30

Вышеупомянутое не сработало для меня из сценария .bash_login, но решение Димитра Радулова $ BASH_SOURCE отлично работает.

John 06.05.2016 22:11

@John Вы имеете в виду .bash_profile? Или как вариант .bashrc? Однако вы должны знать, что есть некоторые тонкие различия в том, как они вызываются / исходят. Я смертельно устал, но если я правильно думаю, это, скорее всего, проблема. В одном месте это имеет значение, если вы входите в систему удаленно, например ssh по сравнению с локальной системой.

Pryftan 22.07.2019 22:43

@Acumenus echo "# my name ------------------> $(cut -d . -f 1 <<<${0##*/}) "

poboxy 18.04.2020 22:29

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

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

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

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

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

Спасибо за имена сценариев sourced.

Fredrick Gauss 11.12.2015 12:11

DIRECTORY=$(cd `dirname $0` && pwd)

Я получил это из другого вопроса Stack Overflow, Может ли сценарий Bash сказать, в каком каталоге он хранится?, но я думаю, что это полезно и для этой темы.

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

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

$(basename $BASH_SOURCE) 

Этот ответ, IMHO, лучший, потому что решение использует самодокументирующийся код. $ BASH_SOURCE полностью понятен без чтения какой-либо документации, тогда как, например, $ {0 ## * /} не является

Andreas M. Oberheim 06.09.2018 15:28

Я считаю, что этот ответ имеет большую ценность, потому что, если мы запустим как . <filename> [arguments], $0 даст имя вызывающей оболочки. Ну, по крайней мере, на OSX точно.

Mihir 15.07.2019 20:48

this = "$(dirname "$(realpath "$BASH_SOURCE")")"

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

FYI realpath не является встроенной командой BASH. Это автономный исполняемый файл, доступный только в определенных дистрибутивах.

StvnW 04.07.2014 17:59

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

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

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

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

BASH_SOURCE

An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}.

FUNCNAME

An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.

This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.

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

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

PSkocik 30.01.2015 15:59

Разве в вашем ответе не указывается ПЕРВЫЙ файл, а не последний / самый последний файл? Я нахожу, что $ {BASH_SOURCE [0]} "или просто $ BASH_SOURCE сообщает мне текущий файл, то есть ответ, опубликованный @Zainka.

rich p 19.06.2020 19:13

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

#!/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
}

Ваше здоровье

что-то вроде этого?

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  ]
then
  val = "*** Unknown  ***"
elif [ -n  ]
then
# otherwise make first arg as val
  val=
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

-1, не отвечает на вопрос (не показывает, как найти имя сценария) и представляет собой запутанный пример очень ошибочного сценария.

Score_Under 28.04.2015 20:32

если вы вызываете сценарий оболочки, например

/home/mike/runme.sh

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

 /home/mike/runme.sh

basename $ 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"

echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"

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

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

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

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

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

Наслаждаться!

Вот что я придумал, вдохновленный ответом Димитр Радулов(кстати, я проголосовал за).

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

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

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

. path/to/script.sh

или же

./path/to/script.sh

Коротко, ясно и просто, в 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.

Это отлично работает с ./self.sh, ~/self.sh, source self.sh, source ~/self.sh:

#!/usr/bin/env bash

self=$(readlink -f "${BASH_SOURCE[0]}")
basename=$(basename "$self")

echo "$self"
echo "$basename"

Кредиты: я объединил несколько ответов, чтобы получить этот.

Другие вопросы по теме