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

390

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

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

Теги:
scripting

19 ответов

423
Лучший ответ
me=`basename "$0"`

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

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

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

  • 27
    $ 0 дает вам имя, через которое был вызван скрипт, а не реальный путь к реальному файлу скрипта.
  • 3
    Это работает, если вас не вызывают по символической ссылке. Но даже тогда, как правило, это то, что вы хотите в любом случае, IME.
Показать ещё 8 комментариев
197
# ------------- 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 ------------- #
  • 8
    Как работает синтаксис ${0%/*} и ${0##*/} ?
  • 9
    @NickC см. Удаление подстроки
Показать ещё 3 комментария
137

С 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"
  • 21
    Большой! Это ответ, который работает как для ./scrip.sh, так и для источника ./script.sh
  • 4
    Это то, что я хочу, и легко использовать « dirname $BASE_SOURCE », чтобы получить каталог, в котором находятся сценарии.
Показать ещё 4 комментария
54

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

  • 2
    (Лучше ответ, чем тот, который получает все голоса!)
  • 9
    (Согласен! Но почему мы говорим в скобках?)
Показать ещё 2 комментария
52

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

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

$(basename $BASH_SOURCE) 
  • 1
    Этот ответ является ИМХО лучшим, потому что решение использует самодокументируемый код. $ BASH_SOURCE полностью понятен без чтения какой-либо документации, тогда как, например, $ {0 ## * /} - нет
19

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

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

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

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

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

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

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

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

9

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

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

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

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

BASH_SOURCE

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

имя_функции

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

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

[Источник: Bash manual]

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

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

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

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

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

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

  • 1
    Хорошие детали, спасибо.
  • 1
    Спасибо за source названия сценариев.
5

если ваша оболочка script вызывает

/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"
5
this="$(dirname "$(realpath "$BASH_SOURCE")")"

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

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

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

basename $0
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
}

Приветствия

1
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
0
DIRECTORY=$(cd `dirname $0` && pwd)

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

0

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

$ cat script.sh
#! /bin/sh
echo `basename $0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

Как получить ./linktoscript для печати script.sh?

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

  • 0
    добавил ответ на это к моему ответу выше, но я не согласен с тем, что это именно то, что действительно нужно (или что вы должны этого хотеть, за исключением некоторых смягчающих обстоятельств, которые я не могу сейчас понять)
  • 2
    Я думаю, что печать linktoscript вместо script.sh - это особенность, а не ошибка. Несколько команд Unix используют свое имя для изменения своего поведения. Примером является vi / ex / view.
Показать ещё 1 комментарий
-2

echo "Вы используете $0"

  • 0
    Это не bash скрипт, это полный путь.
-5

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

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
  • 4
    -1, не отвечает на вопрос (не показывает, как найти имя скрипта) и является запутанным примером в очень глючном скрипте.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню