Как узнать имя файла сценария в сценарии Bash?
Как я могу определить имя файла сценария Bash внутри самого сценария?
Например, если мой скрипт находится в файле runme.sh
тогда как мне сделать так, чтобы отображалось сообщение "Вы запускаете runme.sh" без жесткого кодирования?
27 ответов
me=`basename "$0"`
Для чтения символической ссылки, которая обычно не является тем, что вы хотите (обычно вы не хотите путать пользователя таким образом), попробуйте:
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
ИМО, это даст непонятный результат. "Я запустил foo.sh, но он говорит, что я запускаю bar.sh!? Должно быть, это ошибка!" Кроме того, одной из целей использования символических ссылок с разными именами является предоставление различных функциональных возможностей в зависимости от имени, которое оно называется (например, gzip и gunzip на некоторых платформах).
# ------------- СЦЕНАРИЙ ------------- #
#!/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
# ------------- НАЗЫВАЕТСЯ ------------- #
# Обратите внимание, что на следующей строке первый аргумент вызывается внутри double,
# и одинарные кавычки, так как он содержит два слова$ /misc/shell_scripts/check_root/show_parms.sh "Привет!" "" Уильям "# ------------- РЕЗУЛЬТАТЫ ------------- #
# аргументы вызываются с помощью ---> 'hello there' 'william'
# $1 ----------------------> Привет!
# $2 ----------------------> 'Уильям'
# путь ко мне --------------> /misc/shell_scripts/check_root/show_parms.sh
# родительский путь -------------> /misc/shell_scripts/check_root
# мое имя -----------------> show_parms.sh
# ------------- КОНЕЦ ------------- #
С 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"
$BASH_SOURCE
дает правильный ответ при поиске сценария.
Это, однако, включает путь, чтобы получить только имя файла сценария, используйте:
$(basename $BASH_SOURCE)
Если в имени скрипта есть пробелы, более надежным способом является использование "$0"
или же "$(basename "$0")"
- или на MacOS: "$(basename \"$0\")"
, Это предотвращает искажение или интерпретацию имени любым способом. Как правило, рекомендуется всегда заключать в кавычки имена переменных в оболочке.
Чтобы ответить Крису Конвею, в Linux (по крайней мере) вы должны сделать это:
echo $(basename $(readlink -nf $0))
readlink выводит значение символической ссылки. Если это не символическая ссылка, печатается имя файла. -n говорит не печатать перевод строки. -f говорит, что следует полностью перейти по ссылке (если бы символическая ссылка была ссылкой на другую ссылку, это также разрешило бы эту ссылку).
Поскольку некоторые комментарии спрашивают о имени файла без расширения, вот пример, как это сделать:
FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}
Наслаждайтесь!
Я обнаружил, что эта строка всегда работает, независимо от того, используется ли файл в качестве сценария.
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]
Эти ответы являются правильными для случаев, которые они заявляют, но все еще остается проблема, если вы запускаете сценарий из другого сценария, используя ключевое слово "source" (чтобы он выполнялся в той же оболочке). В этом случае вы получаете $0 вызывающего скрипта. И в этом случае я не думаю, что возможно получить название самого сценария.
Это крайний случай, и к нему не следует относиться серьезно. Если вы запустите скрипт из другого скрипта напрямую (без 'source'), будет работать $0.
Это отлично работает с
./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"
Кредиты: я объединил несколько ответов, чтобы получить этот.
Re: ответ Tanktalus (принятый) выше, более чистый способ заключается в использовании:
me=$(readlink --canonicalize --no-newline $0)
Если ваш скрипт был получен из другого скрипта bash, вы можете использовать:
me=$(readlink --canonicalize --no-newline $BASH_SOURCE)
Я согласен, что было бы непонятным разыменовывать символические ссылки, если ваша цель - предоставить обратную связь пользователю, но бывают случаи, когда вам нужно получить каноническое имя для сценария или другого файла, и это лучший способ, imo.
this="$(dirname "$(realpath "$BASH_SOURCE")")"
Это разрешает символические ссылки (это делает realpath), обрабатывает пробелы (это делают двойные кавычки) и находит текущее имя скрипта, даже когда он получен (. ./Myscript) или вызван другими скриптами ($BASH_SOURCE обрабатывает это). После всего этого полезно сохранить это в переменной среды для повторного использования или для простого копирования в другом месте (this=)...
Вы можете использовать $0, чтобы определить имя вашего скрипта (с полным путем) - чтобы получить имя скрипта, вы можете только обрезать эту переменную с помощью
basename $0
Если ваш вызов сценария оболочки, как
/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"
В bash
Вы можете получить имя файла скрипта, используя $0
, В общем-то $1
, $2
и т. д. для доступа к аргументам CLI. так же $0
является доступ к имени, которое запускает сценарий (имя файла сценария).
#!/bin/bash
echo "You are running $0"
...
...
Если вы вызываете скрипт с путем, как /path/to/script.sh
затем $0
также даст имя файла с путем. В этом случае необходимо использовать $(basename $0)
получить только имя файла скрипта.
Коротко, ясно и просто, в 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.
$0
не отвечает на вопрос (насколько я понимаю). Демонстрация:
$ cat script.sh #! / Бен / ш echo `basename $0` $ ./script.sh script.sh $ ln script.sh linktoscript $ ./linktoscript linktoscript
Как можно получить ./linktoscript
распечатать script.sh
?
[EDIT] В @ephemient в комментариях выше, хотя символическая ссылка может показаться надуманной, можно поиграть с $0
такой, что он не представляет ресурс файловой системы. ОП немного двусмысленно о том, что он хотел.
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
&& readlink ${BASH_SOURCE[0]} \
|| echo ${BASH_SOURCE[0]}`")"
Информация благодаря Биллу Эрнандесу. Я добавил некоторые предпочтения, которые я принимаю.
#!/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
}
ура
Чтобы получить «реальный путь» сценария или исходных сценариев во всех случаях:
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
Вот сценарий bash для создания (во вновь созданном подкаталоге «workdir» и в « mytest » в текущем каталоге) сценарий bash, который, в свою очередь, будет источником другого сценария, который, в свою очередь, вызовет функцию, определенную bash.... проверено многими способами их запуска:
#!/bin/bash
##############################################################
ret=0
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
fullname_withoutextension=${fullname%.*}
mkdir -p workdir
cat <<'EOD' > workdir/_script_.sh
#!/bin/bash
##############################################################
ret=0
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
fullname_withoutextension=${fullname%.*}
echo
echo "# ------------- RESULTS ------------- #"
echo "# path to me (\$0)-----------> ${0} "
echo "# arguments called with ----> ${@} "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me (\$fullname)----> ${fullname} "
echo "# parent path(\${0%/*})------> ${0%/*} "
echo "# parent path(\$dirname)-----> ${dirname} "
echo "# my name ----\${0##*/}------> ${0##*/} "
echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} "
echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}"
echo "# my source or script real path (realpath)------------------> $realpath"
echo
[ "$realpath" = "workdir" ] || ret=1
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
show_params () {
echo
echo "# --- RESULTS FROM show_params() ---- #"
echo "# path to me (\$0)-----------> ${0} "
echo "# arguments called with ----> ${@} "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me (\$fullname)----> ${fullname} "
echo "# parent path(\${0%/*})------> ${0%/*} "
echo "# parent path(\$dirname)-----> ${dirname} "
echo "# my name ----\${0##*/}------> ${0##*/} "
echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} "
echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}"
echo "# my source or script real path (realpath)------------------> $realpath"
echo
[ "$realpath" = "workdir" ] || ret=1
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
}
show_params "$@"
EOD
cat workdir/_script_.sh > workdir/_side_by_side_script_sourced.inc
cat <<'EOD' >> workdir/_script_.sh
echo "# . $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'"
. $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
EOD
chmod +x workdir/_script_.sh
[ -L _mytest_ ] && rm _mytest_
ln -s workdir/_script_.sh _mytest_
# ------------- CALLED ------------- #
called_by () {
echo '=========================================================================='
echo " Called by : " "$@"
echo '=========================================================================='
eval "$@"
}
called_by bash _mytest_
called_by ./_mytest_
called_by bash workdir/_script_.sh
called_by workdir/_script_.sh
called_by . workdir/_script_.sh
# ------------- RESULTS ------------- #
echo
echo
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
echo
[ $ret = 0 ] && echo ".... location of scripts (\$realpath) should always be equal to $realpath, for all test cases at date".
echo
# ------------- END ------------- #
Вот то, что я придумал, вдохновленный Dimitre Radoulov(кстати, я проголосовал).
script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"
echo "Called $script with $# argument(s)"
независимо от того, как вы называете свой сценарий
. path/to/script.sh
или же
./path/to/script.sh
В качестве опции, не указанной в ответах выше, вы можете использовать pid скрипта, чтобы получить много информации, включая имя, из/proc/$pid
папка, вот так:
#!/bin/bash
pid=$$ # get script's pid to var $pid
echo $pid
# there is a file in /proc/$pid folder called cmdline
# this file stores full command line used by script
cat /proc/$pid/cmdline
выход:
$ ./test 1 '2 3' 4
12924
/bin/bash./test12 34
вывод выглядит немного беспорядочно, это всего лишь одна строка... не совсем так, чтобы на самом деле у нее были разделители, но чтобы иметь возможность их видеть, нам нужно изменить нашуcat
команда такая:
#!/bin/bash
pid=$$ # get script's pid to var $pid
echo $pid
# add -A - show all option for cat
cat -A /proc/$pid/cmdline
вот и все, теперь мы можем видеть разделители^@
:
$ ./test 1 '2 3' 4
13002
/bin/bash^@./test^@1^@2 3^@4^@
давайте добавим еще немного взлома, чтобы получить имя:
#!/bin/bash
pid=$$ # get script's pid to var $pid
# tr to change '^@' into ' ' and awk to print 2nt column(script name)
name=$(cat -A /proc/$pid/cmdline | tr '^@' ' ' | awk '{print $2}')
echo "
pid: $pid
name: $name
"
результат:
$ ./test 1 '2 3' 4
pid: 13091
name: ./test
$0 даст имя скрипта, который вы запускаете. Создайте файл сценария и добавьте следующий код
#!/bin/bash
echo "Name of the file is $0"
затем запустите с терминала вот так
./file_name.sh
DIRECTORY=$(cd `dirname $0` && pwd)
Я получил вышеупомянутое от другого вопроса переполнения стека, может ли скрипт Bash сказать, в каком каталоге он хранится?, но я думаю, что это полезно и для этой темы.
Что -то подобное?
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