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





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 дает вам имя, через которое был вызван сценарий, а не реальный путь к фактическому файлу сценария.
Это работает, если вас не вызывают по символической ссылке. Но даже в этом случае это обычно то, что вам нужно, IME.
Вы можете быть вызваны по имени, которое вообще не существует в виде файла - наиболее распространенный пример: вызов sh как "- / bin / sh" заставляет его действовать как оболочку входа в систему. См. exec -a в Bash или execvp("/bin/foo", {"blah", ...}).
Да, это лучший пример.
Если ваш скрипт изменяет свое поведение в зависимости от того, как он вызывается, неплохо было бы гарантировать, что при вычислении $ 0 какой-нибудь уловит все ... в противном случае простая символическая ссылка может заставить его делать странные вещи :) Т.е. если ссылка была названа foobar, а сценарий учитывает только foo или bar.
Не работает для сценариев, которые исходят, а не вызываются.
@ Чарльз Даффи: см. Ответ Димитра Радулова ниже.
-1, 1. readlink будет перемещаться только на одну символическую ссылку в глубину, 2. $0 в первом примере подвержен разделению на слова, 3. $0 передается в basename, readlink и echo в позиции, которая позволяет рассматривать его как команду линейный переключатель. Я предлагаю вместо этого me=$(basename -- "$0") или, что гораздо эффективнее за счет читабельности, me=${0##*/}. Для символических ссылок me=$(basename -- "$(readlink -f -- "$0")") предполагает использование gnu utils, иначе это будет очень длинный сценарий, который я не буду здесь писать.
echo "У вас $ 0"
Это не сценарий bash, это полный путь.
Вы можете использовать $ 0 для определения имени вашего скрипта (с полным путем) - чтобы получить только имя скрипта, вы можете обрезать эту переменную с помощью
basename $0
Если в имени сценария есть пробелы, более надежным способом является использование "$0" или "$(basename "$0")" - или в MacOS: "$(basename \"$0\")". Это предотвращает искажение или интерпретацию имени каким-либо образом. В общем, рекомендуется всегда заключать имена переменных в двойные кавычки в оболочке.
+1 за прямой ответ + лаконичный. Если вам нужны функции символической ссылки, я предлагаю посмотреть: Ответ Трэвиса Б. Хартвелла.
Чтобы ответить Крис Конвей, в Linux (по крайней мере) вы должны сделать это:
echo $(basename $(readlink -nf $0))
readlink выводит значение символической ссылки. Если это не символическая ссылка, печатается имя файла. -n указывает ему не печатать новую строку. -f указывает ему полностью перейти по ссылке (если символическая ссылка была ссылкой на другую ссылку, она также разрешит эту ссылку).
-n безвреден, но не обязателен, потому что структура $ (...) обрежет его.
Если вы хотите его без пути, вы должны использовать ${0##*/}
А что, если я хочу его без дополнительного расширения файла?
Чтобы удалить расширение, вы можете попробовать «$ {VARIABLE% .ext}», где VARIABLE - это значение, полученное вами из $ {0 ## * /}, а «.ext» - это расширение, которое вы хотите удалить.
Эти ответы верны для заявленных случаев, но проблема по-прежнему сохраняется, если вы запускаете сценарий из другого сценария с ключевым словом 'source' (чтобы он выполнялся в той же оболочке). В этом случае вы получите $ 0 вызывающего скрипта. И в этом случае я не думаю, что возможно получить название самого скрипта.
Это крайний случай, и его не следует воспринимать СЛИШКОМ серьезно. Если вы запустите сценарий напрямую из другого сценария (без «источника»), использование $ 0 будет работать.
У вас очень хорошая мысль. Не крайний случай ИМО. Однако есть решение: см. Ответ Димитра Радулова выше
С 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
Это то, что я хочу, и легко использовать "dirname $BASE_SOURCE", чтобы получить каталог, в котором находятся скрипты.
Я почти осознал эту разницу на собственном горьком опыте, когда писал самоудаляющийся скрипт. К счастью, у 'rm' был псевдоним 'rm -i' :)
в любом случае в. ./s, чтобы получить имя ./s вместо bash? Я обнаружил, что $ 1 не всегда устанавливается на ./s ...
Это в BASH_SOURCE, не так ли?
BASH_SOURCE - это массив, и его нельзя использовать таким образом.
@kervin Осторожно с этим ... Это действительно похоже на присмотр за детьми, а что произойдет, если вы забудете настроить его в системе, в которой его нет по умолчанию? Я лично отключил его на всех своих системах. Это чертовски неприятно, и другой -Я (один раз интерактивный) не намного лучше для меня. Кроме того, если я говорю, что хочу что-то удалить, я не хочу, чтобы меня спрашивали, уверен ли я в этом. Конечно, я в этом уверен! И даже если я введу неверный путь из-за опечатки, скажите, что есть такая вещь, как «резервное копирование». В любом случае, дело в том, что есть вероятность, что это не во всех системах и может быть катастрофическим.
@Pryftan тогда какие альтернативы?
# ------------- 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 ------------- #
Как мне получить только show_params, то есть имя без дополнительного расширения?
Не работает, если скрипт вызывается из другой папки. Путь включен в ${0##*/}. Протестировано с использованием GitBash.
Вышеупомянутое не сработало для меня из сценария .bash_login, но решение Димитра Радулова $ BASH_SOURCE отлично работает.
@John Вы имеете в виду .bash_profile? Или как вариант .bashrc? Однако вы должны знать, что есть некоторые тонкие различия в том, как они вызываются / исходят. Я смертельно устал, но если я правильно думаю, это, скорее всего, проблема. В одном месте это имеет значение, если вы входите в систему удаленно, например ssh по сравнению с локальной системой.
@Acumenus echo "# my name ------------------> $(cut -d . -f 1 <<<${0##*/}) "
Re: (принятый) ответ Tanktalus выше, немного более чистый способ - использовать:
me=$(readlink --canonicalize --no-newline $0)
Если ваш сценарий был получен из другого сценария bash, вы можете использовать:
me=$(readlink --canonicalize --no-newline $BASH_SOURCE)
Я согласен с тем, что разыменование символических ссылок могло бы сбить с толку, если ваша цель - предоставить обратную связь пользователю, но бывают случаи, когда вам действительно нужно получить каноническое имя для скрипта или другого файла, и это лучший способ, imo.
Спасибо за имена сценариев sourced.
DIRECTORY=$(cd `dirname $0` && pwd)
Я получил это из другого вопроса Stack Overflow, Может ли сценарий Bash сказать, в каком каталоге он хранится?, но я думаю, что это полезно и для этой темы.
$BASH_SOURCE дает правильный ответ при поиске скрипта.
Однако это включает в себя путь, поэтому, чтобы получить только имя файла сценария, используйте:
$(basename $BASH_SOURCE)
Этот ответ, IMHO, лучший, потому что решение использует самодокументирующийся код. $ BASH_SOURCE полностью понятен без чтения какой-либо документации, тогда как, например, $ {0 ## * /} не является
Я считаю, что этот ответ имеет большую ценность, потому что, если мы запустим как . <filename> [arguments], $0 даст имя вызывающей оболочки. Ну, по крайней мере, на OSX точно.
this = "$(dirname "$(realpath "$BASH_SOURCE")")"
Это разрешает символические ссылки (это делает realpath), обрабатывает пробелы (это делают двойные кавычки) и находит текущее имя сценария, даже если оно получено (./myscript) или вызвано другими сценариями ($ BASH_SOURCE обрабатывает это). После всего этого хорошо сохранить это в переменной среды для повторного использования или для простого копирования в другом месте (this =) ...
FYI realpath не является встроенной командой BASH. Это автономный исполняемый файл, доступный только в определенных дистрибутивах.
Я обнаружил, что эта строка работает всегда, независимо от того, был ли файл получен или запущен как сценарий.
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.
Разве в вашем ответе не указывается ПЕРВЫЙ файл, а не последний / самый последний файл? Я нахожу, что $ {BASH_SOURCE [0]} "или просто $ BASH_SOURCE сообщает мне текущий файл, то есть ответ, опубликованный @Zainka.
Информация благодаря Биллу Эрнандесу. Я добавил некоторые настройки, которые принимаю.
#!/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, не отвечает на вопрос (не показывает, как найти имя сценария) и представляет собой запутанный пример очень ошибочного сценария.
если вы вызываете сценарий оболочки, например
/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"
Кредиты: я объединил несколько ответов, чтобы получить этот.
Подобно [Может ли сценарий bash сказать, в каком каталоге он хранится?] (К stackoverflow.com/questions/59895/…)