Як я знаю, що ім'я файлу сценарію в сценарії Bash?


605

Як я можу визначити ім'я файлу сценарію Bash всередині самого сценарію?

Як, якщо мій сценарій у файлі runme.sh, то як би я змусив його відображати повідомлення "Ти працюєш runme.sh" без жорсткого кодування цього?


3
Подібне [Чи може сценарій bash розповісти, в якому каталозі він зберігається?] ( To stackoverflow.com/questions/59895/… )
Rodrigue,

Відповіді:


630
me=`basename "$0"`

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

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

IMO, що дасть заплутаний результат. "Я біг foo.sh, але це говорить про те, що я запускаю bar.sh! Повинна бути помилка!" Крім того, однією з цілей створення по-різному названих символьних посилань є надання різної функціональності на основі імені, яке воно називається (подумайте, gzip та gunzip на деяких платформах).


1 Тобто для вирішення символьних посилань таким чином, що коли користувач виконує, foo.shщо є насправді символьним посиланням на bar.sh, ви хочете скористатись розв’язаним ім'ям, bar.shа не foo.sh.


40
$ 0 дає вам ім'я, через яке було викликано сценарій, а не реальний шлях до фактичного файлу сценарію.
Кріс Конвей

4
Це працює, якщо вас не викликають через symlink. Але навіть тоді це, як правило, те, що ти хочеш у будь-якому випадку, IME.
Tanktalus

78
Не працює для скриптів, які створені на відміну від викликаних.
Чарльз Даффі

1
@Charles Duffy: Дивіться відповідь Димитра Радулова нижче .
Серж Вотьє

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

246
# ------------- СКРИПТ ------------- # # ------------- ЗВАННІ ------ ------- #

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



# Помітьте, що в наступному рядку перший аргумент викликається в подвійних, # та одинарних лапках, оскільки він містить два слова


$   / misc / shell_scripts / check_root / show_parms . sh "'привіт там" "" william "" 

# ------------- РЕЗУЛЬТАТИ ------------- #

# аргументи, викликані ---> "привіт, там" "william" # $ 1 ----------------------> "привіт, там" # $ 2 ---- ------------------> 'william' # шлях до мене --------------> / misc / shell_scripts / check_root / show_parms. sh # батьківський шлях -------------> / misc / shell_scripts / check_root # моє ім'я -----------------> show_parms.sh






# ------------- КОНЕЦ ------------- #


1
Як отримати просто show_params, тобто ім'я без будь-якого додаткового розширення?
Acumenus

Не працює, якщо сценарій викликається з іншої папки. Шлях включений у ${0##*/}. Тестували за допомогою GitBash.
AlikElzin-kilaka

1
Вищезгадане не працювало для мене зі сценарію .bash_login, але рішення Димитра Радулова в $ BASH_SOURCE працює чудово.
Іван

@John Звичайно, ти маєш на увазі .bash_profile ? Або альтернативно .bashrc ? Ви повинні знати, що є деякі тонкі відмінності в тому, як вони викликаються / отримуються джерела. Я мертвий втомився, але якщо я думаю, що це було б цілком ймовірно, проблема. Важливо одне місце, якщо ви віддалено входите в систему, наприклад, ssh порівняно з локальною системою.
Прифтан

193

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

24
Чудово! Це відповідь, яка працює як для ./scrip.sh, так і для джерела ./script.sh
zhaorufei

4
Це те, що я хочу, і це легко використовувати, dirname $BASE_SOURCEщоб отримати " " каталог, в якому розміщені сценарії.
Ларрі Кай

2
Я майже навчився цієї різниці важким способом, коли писав сценарій, що самозаймається. На щастя, 'rm' був псевдонім на 'rm -i' :)
kervin

все одно до. ./s, щоб отримати ім’я ./s замість bash? Я виявив, що $ 1 не завжди встановлюється ./s ...
Девід Мокон Бонд

1
Це в BASH_SOURCE , чи не так?
Димитрій Радулов

69

$BASH_SOURCE дає правильну відповідь під час пошуку сценарію.

Однак це включає шлях, щоб отримати лише ім'я файлу скриптів, використовуйте:

$(basename $BASH_SOURCE) 

2
Ця відповідь є IMHO найкращою, оскільки в цьому рішенні використовується код самодокументування. $ BASH_SOURCE цілком зрозумілий, не читаючи жодної документації, тоді як, наприклад, $ {0 ## * /} немає
Andreas M. Oberheim

Ця відповідь має більшу цінність я вірую, тому що якщо ми біжимо так . <filename> [arguments], $0то дамо ім'я оболонки абонента. Ну принаймні на OSX точно.
Mihir

68

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


2
+1 для прямої відповіді + стисло. якщо потрібні функції симпосилання, я пропоную побачити: відповідь Тревіса Б. Хартвелла.
Тревор Бойд Сміт

26

Якщо ви хочете його без шляху, то ви б скористалися ${0##*/}


А що робити, якщо я хочу його без будь-якого додаткового розширення файлу?
Acumenus

Щоб видалити розширення, ви можете спробувати "$ {VARIABLE% .ext}", де VARIABLE - це значення, яке ви отримали від $ {0 ## * /}, а ".ext" - розширення, яке ви хочете видалити.
BrianV

19

Щоб відповісти на Chris Conway , в Linux (принаймні) ви зробите це:

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

readlink виводить значення символічного посилання. Якщо це не символічне посилання, воно друкує ім'я файлу. -n каже йому не друкувати новий рядок. -f повідомляє йому повністю переходити за посиланням (якщо символічне посилання було посиланням на інше посилання, воно також вирішило б це).


2
-n нешкідливий, але не потрібний, оскільки $ (...) структура його обріже.
zhaorufei

16

Я виявив, що цей рядок завжди працює, незалежно від того, чи файл розміщується або працює як сценарій.

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

Якщо ви хочете дотримуватись посилань, використовуйте readlinkна шляху, який ви переходите вище, рекурсивно чи не рекурсивно.

Причина роботи однолінійки пояснюється використанням BASH_SOURCEзмінної середовища та її асоційованого елемента FUNCNAME.

BASH_SOURCE

Змінна масиву, членами якої є джерела імен файлів, де визначені відповідні імена функції оболонки в змінній масиву FUNCNAME. Функція оболонки $ {FUNCNAME [$ i]} визначена у файлі $ {BASH_SOURCE [$ i]} і викликана від $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Змінна масиву, що містить імена всіх функцій оболонки, що знаходяться в стеці виклику виконання. Елемент з індексом 0 - назва будь-якої функції оболонки, яка виконується в даний час. Найнижчий елемент (той, що має найвищий показник) - "головний". Ця змінна існує лише тоді, коли виконується функція оболонки. Призначення FUNCNAME не мають ефекту і повертають статус помилки. Якщо FUNCNAME не встановлено, він втрачає свої особливі властивості, навіть якщо він згодом скидається.

Цю змінну можна використовувати з BASH_LINENO та BASH_SOURCE. Кожен елемент FUNCNAME має відповідні елементи в BASH_LINENO та BASH_SOURCE для опису стеку викликів. Наприклад, $ {FUNCNAME [$ i]} викликався з файлу $ {BASH_SOURCE [$ i + 1]} за номером рядка $ {BASH_LINENO [$ i]}. Вбудований абонент відображає поточний стек викликів, використовуючи цю інформацію.

[Джерело: Посібник Bash]


2
Це працює, якщо ви надсилаєте файл у файл a (припустимо, aйого вміст echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") з інтерактивного сеансу - тоді він дасть вам aшлях. Але якщо ви напишете сценарій bз source aним і запустите ./b, він поверне bшлях.
PSkocik

12

Ці відповіді є правильними для випадків, в яких вони констатують, але все ще існує проблема, якщо запустити сценарій з іншого сценарію за допомогою ключового слова "source" (щоб він працював в одній оболонці). У цьому випадку ви отримуєте 0 $ скрипта виклику. І в цьому випадку я не думаю, що можна назвати сам сценарій.

Це кращий випадок, і його не слід сприймати серйозно. Якщо ви запускаєте сценарій з іншого сценарію безпосередньо (без "джерела"), використання $ 0 буде працювати.


2
У вас дуже хороший момент. Не крайній випадок ІМО. Однак є рішення: Дивіться відповідь Димитра Радулова вище
Серж Вотьє,

10

Оскільки деякі коментарі запитували про ім’я файлу без розширення, ось приклад, як це зробити:

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

Насолоджуйтесь!


9

Re: Відповідь Танкталуса (прийнято) вище, трохи більш чистим способом є використання:

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

Якщо ваш скрипт отримано з іншого сценарію bash, ви можете використовувати:

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

Я погоджуюсь, що посилання на дереференцію буде заплутаною, якщо вашою метою є надання відгуку користувачеві, але бувають випадки, коли вам потрібно отримати канонічне ім'я до сценарію чи іншого файлу, і це найкращий спосіб, imo.


1
Дякуємо за sourceімена сценарію d.
Фредрік Гаус

8

Ви можете використовувати $ 0, щоб визначити назву сценарію (з повним контуром) - щоб отримати ім'я сценарію лише ви можете обрізати цю змінну

basename $0

8

якщо ваш сценарій оболонки виклику, як

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

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

Це вирішує символічні посилання (realpath робить це), обробляє пробіли (подвійні лапки роблять це) і знайде поточне ім'я сценарію навіть тоді, коли вони розміщені (./myscript) або викликаються іншими сценаріями ($ BASH_SOURCE обробляє це). Зрештою, це добре зберегти в змінній середовища для повторного використання або для простого копіювання в іншому місці (це =) ...


3
FYI realpath- це не вбудована команда BASH. Це автономний виконуваний файл, який доступний лише у певних дистрибутивах
StvnW

4

В bash ви можете отримати ім'я файлу сценарію з допомогою $0. Як правило $1, $2тощо - це доступ до аргументів CLI. Аналогічним чином $0є доступ до імені, яке запускає скрипт (назва файлу сценарію).

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

Якщо ви викликаєте сценарій таким шляхом, як /path/to/script.shтоді$0 також дасте ім'я файлу з шляхом. У такому випадку потрібно використовувати $(basename $0)лише ім'я файлу сценарію.


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

2

$0не відповідає на питання (наскільки я це розумію). Демонстрація:

$ cat script.sh
#! / бін / ш
echo `базове ім'я $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

Як можна отримати ./linktoscriptдрук script.sh?

[EDIT] За коментарями вище @ephemient, хоча символічна посилання може здатися надуманою, можна зіткнутися з $0таким, що не представляє ресурс файлової системи. ОП трохи неоднозначно щодо того, чого він хотів.


на цю відповідь я додав відповідь вище, але я не погоджуюся, що це саме те, що дуже хочеться (або що ви хочете цього хотіти, забороняючи деякі пом'якшуючі обставини, про які я зараз не можу зрозуміти)
Tanktalus

2
Я думаю, що друкувати linktoscript замість script.sh - це особливість, а не помилка. Кілька команд unix використовують своє ім'я для зміни своєї поведінки. Приклад - vi / ex / view.
mouviciel

@Tanktalus: Є випадки, коли ви хочете, щоб поведінка змінилася, виходячи з реального розташування файлу - у попередньому проекті у мене був сценарій "Активна гілка", який міг би позначити шлях до декількох інструментів із гілки, в якій я був працює на; ці інструменти могли потім дізнатись, у який каталог вони перевірені, щоб працювати із потрібними файлами.
Ендрю Ейлетт

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

Короткий, зрозумілий і простий, в 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

Ось що я придумав, натхненний Dimitre Radoulov відповідь «s (який я upvoted, до речі) .

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

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

незалежно від того, як ви називаєте свій сценарій

. path/to/script.sh

або

./path/to/script.sh


-6

щось таке?

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

5
-1, не відповідає на запитання (не показує, як знайти ім'я сценарію), і є заплутаним прикладом у дуже баггі-скрипті.
Score_Under
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.