Как узнать имя файла сценария в сценарии 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\")", Это предотвращает искажение или интерпретацию имени любым способом. Как правило, рекомендуется всегда заключать в кавычки имена переменных в оболочке.

Если вы хотите это без пути, то вы бы использовали ${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 сказать, в каком каталоге он хранится?, но я думаю, что это полезно и для этой темы.

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

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

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
Другие вопросы по тегам