Làm cách nào để biết tên tệp tập lệnh trong tập lệnh Bash?


606

Làm cách nào để xác định tên của tệp script Bash bên trong chính script?

Giống như nếu tập lệnh của tôi nằm trong tệp runme.shthì tôi sẽ làm thế nào để hiển thị thông báo "Bạn đang chạy runme.sh" mà không cần mã hóa?


3
Tương tự [Tập lệnh bash có thể cho biết thư mục nào được lưu trữ trong đó không?] (Đến stackoverflow.com/questions/59895/ mẹo )
Coleue

Câu trả lời:


631
me=`basename "$0"`

Để đọc qua symlink 1 , thường không phải là điều bạn muốn (bạn thường không muốn gây nhầm lẫn cho người dùng theo cách này), hãy thử:

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

IMO, điều đó sẽ tạo ra đầu ra khó hiểu. "Tôi đã chạy foo.sh, nhưng nó nói rằng tôi đang chạy bar.sh!? Phải là một lỗi!" Ngoài ra, một trong những mục đích của việc có các liên kết tượng trưng được đặt tên khác nhau là cung cấp các chức năng khác nhau dựa trên tên mà nó được gọi là (nghĩ gzip và gunzip trên một số nền tảng).


1 Nghĩa là, để giải quyết các liên kết tượng trưng để khi người dùng thực thi foo.shđó là một liên kết tượng trưng bar.sh, bạn muốn sử dụng tên đã giải quyết bar.shhơn là foo.sh.


40
$ 0 cung cấp cho bạn tên mà qua đó tập lệnh được gọi, không phải là đường dẫn thực sự của tệp tập lệnh thực tế.
Chris Conway

4
Nó hoạt động trừ khi bạn được gọi thông qua symlink. Nhưng, ngay cả khi đó, đó thường là những gì bạn muốn, IME.
Tanktalus

78
Không hoạt động đối với các tập lệnh có nguồn gốc trái ngược với yêu cầu.
Charles Duffy


8
-1, 1. readlink sẽ chỉ di chuyển sâu một symlink, 2. $0trong ví dụ đầu tiên có thể phân tách từ, 3. $0được chuyển đến tên cơ sở, readlink và echo ở vị trí cho phép nó được coi là một chuyển đổi dòng lệnh . Tôi đề nghị thay thế me=$(basename -- "$0")hoặc hiệu quả hơn nhiều với chi phí dễ đọc , me=${0##*/}. Đối với các liên kết tượng trưng, me=$(basename -- "$(readlink -f -- "$0")")giả sử các tiện ích gnu, nếu không nó sẽ là một kịch bản rất dài mà tôi sẽ không viết ở đây.
Điểm_Under

246
# ------------- SCRIPT ------------- # # ------------- GỌI ------ ------- #

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



# Lưu ý trên dòng tiếp theo, đối số đầu tiên được gọi trong dấu ngoặc kép, # và dấu ngoặc đơn, vì nó chứa hai từ


$   / misc / shell_scripts / check_root / show_parms . sh "'xin chào'" "'william'" 

# ------------- CÁC KẾT QUẢ ------------- #

# đối số được gọi với ---> 'xin chào' 'william' # $ 1 ----------------------> 'xin chào' # $ 2 ---- ------------------> 'william' # đường dẫn đến tôi --------------> / misc / shell_scripts / check_root / show_parms. sh # đường dẫn cha mẹ -------------> / misc / shell_scripts / check_root # tên tôi -----------------> show_parms.sh






# ------------- KẾT THÚC ------------- #

11
@NickC xem Xóa chuỗi con
cychoi

1
Làm thế nào để tôi có được chỉ show_params, tức là tên mà không có bất kỳ phần mở rộng tùy chọn?
Acumenus

Không hoạt động trong trường hợp tập lệnh được gọi từ một thư mục khác. Các đường dẫn được bao gồm trong ${0##*/}. Đã thử nghiệm bằng GitBash.
AlikElzin-kilaka

1
Những điều trên không phù hợp với tôi từ tập lệnh .bash_login, nhưng giải pháp Dimitre Radoulov của $ BASH_SOURCE hoạt động rất tốt.
Giăng

@ John Chắc chắn ý bạn là .bash_profile ? Hoặc cách khác .bashrc ? Mặc dù vậy, bạn nên biết rằng có một số khác biệt tinh tế trong cách chúng được gọi / nguồn gốc. Tôi chết mê chết mệt nhưng nếu tôi nghĩ đúng thì đó có thể là vấn đề. Một vấn đề quan trọng là nếu bạn đăng nhập vào một hệ thống từ xa, ví dụ như ssh so với tại hệ thống cục bộ.
Pryftan

193

Với bash> = 3 công việc sau:

$ ./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
Tuyệt quá! Đó là câu trả lời phù hợp với cả ./scrip.sh và nguồn
./script.sh

4
Đây là những gì tôi muốn và thật dễ dàng sử dụng " dirname $BASE_SOURCE" để lấy thư mục chứa các tập lệnh.
Larry Cai

2
Tôi gần như đã học được sự khác biệt đó một cách khó khăn khi viết một kịch bản tự xóa. May mắn thay 'rm' được đặt bí danh là 'rm -i' :)
kervin

dù sao đến ./s để lấy tên ./s thay vì bash? Tôi đã thấy rằng $ 1 không phải lúc nào cũng được đặt thành ./s ...
David Mokon Bond

1
Nó ở BASH_SOURCE , phải không?
Dimitre Radoulov

69

$BASH_SOURCE đưa ra câu trả lời đúng khi tìm nguồn cung ứng kịch bản.

Tuy nhiên, điều này bao gồm đường dẫn để chỉ lấy tên tệp script, sử dụng:

$(basename $BASH_SOURCE) 

2
Câu trả lời này là IMHO tốt nhất vì giải pháp này sử dụng mã tự ghi. $ BASH_SOURCE hoàn toàn dễ hiểu mà không cần đọc bất kỳ tài liệu nào trong khi ví dụ $ {0 ## * /} thì không
Andreas M. Oberheim

Câu trả lời này có giá trị hơn tôi tin bởi vì nếu chúng ta chạy như thế . <filename> [arguments], $0sẽ đặt tên của vỏ người gọi. Ít nhất là trên OSX chắc chắn.
Mihir

68

Nếu tên tập lệnh có khoảng trắng trong đó, một cách mạnh mẽ hơn là sử dụng "$0"hoặc "$(basename "$0")"- hoặc trên MacOS : "$(basename \"$0\")". Điều này ngăn tên bị xáo trộn hoặc diễn giải theo bất kỳ cách nào. Nói chung, đó là một thực hành tốt để luôn luôn trích dẫn hai tên biến trong shell.


2
+1 cho câu trả lời trực tiếp + súc tích. nếu cần các tính năng liên kết tượng trưng, ​​tôi khuyên bạn nên xem: Câu trả lời của Travis B. Hartwell.
Trevor Boyd Smith

26

Nếu bạn muốn nó mà không có đường dẫn thì bạn sẽ sử dụng ${0##*/}


Và nếu tôi muốn nó mà không có bất kỳ phần mở rộng tập tin tùy chọn thì sao?
Acumenus

Để xóa tiện ích mở rộng, bạn có thể thử "$ {VARIABLE% .ext}" trong đó VARIABLE là giá trị bạn nhận được từ $ {0 ## * /} và ".ext" là tiện ích mở rộng bạn muốn xóa.
BrianV

19

Để trả lời Chris Conway , trên Linux (ít nhất) bạn sẽ làm điều này:

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

readlink in ra giá trị của một liên kết tượng trưng. Nếu nó không phải là một liên kết tượng trưng, ​​nó sẽ in tên tệp. -n bảo nó không in một dòng mới. -f bảo nó hoàn toàn theo liên kết (nếu một liên kết tượng trưng là một liên kết đến một liên kết khác, nó cũng sẽ giải quyết liên kết đó).


2
-n là vô hại nhưng không cần thiết vì cấu trúc $ (...) sẽ cắt nó.
zhaorufei

16

Tôi đã tìm thấy dòng này luôn hoạt động, bất kể tập tin đang được lấy nguồn hay chạy dưới dạng tập lệnh.

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

Nếu bạn muốn theo liên kết sym sử dụng readlink trên đường dẫn bạn nhận được ở trên, đệ quy hoặc không đệ quy.

Lý do các công việc một lớp được giải thích bằng việc sử dụng BASH_SOURCEbiến môi trường và liên kết của nó FUNCNAME.

BASH_SOURCE

Một biến mảng có thành viên là tên tệp nguồn trong đó tên hàm shell tương ứng trong biến mảng FUNCNAME được xác định. Hàm shell $ {FUNCNAME [$ i]} được xác định trong tệp $ {BASH_SOURCE [$ i]} và được gọi từ $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Một biến mảng chứa tên của tất cả các hàm shell hiện có trong ngăn xếp lệnh thực thi. Phần tử có chỉ số 0 là tên của bất kỳ hàm shell nào đang thực thi. Phần tử dưới cùng nhất (phần tử có chỉ số cao nhất) là "chính". Biến này chỉ tồn tại khi một hàm shell đang thực thi. Việc gán cho FUNCNAME không có hiệu lực và trả về trạng thái lỗi. Nếu FUNCNAME không được đặt, nó sẽ mất các thuộc tính đặc biệt, ngay cả khi sau đó được đặt lại.

Biến này có thể được sử dụng với BASH_LINENO và BASH_SOURCE. Mỗi phần tử của FUNCNAME có các phần tử tương ứng trong BASH_LINENO và BASH_SOURCE để mô tả ngăn xếp cuộc gọi. Chẳng hạn, $ {FUNCNAME [$ i]} đã được gọi từ tệp $ {BASH_SOURCE [$ i + 1]} tại số dòng $ {BASH_LINENO [$ i]}. Trình dựng cuộc gọi hiển thị ngăn xếp cuộc gọi hiện tại bằng cách sử dụng thông tin này.

[Nguồn: hướng dẫn Bash]


2
Nó hoạt động nếu bạn nguồn trong tệp a (giả sử anội dung là echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") từ một phiên tương tác - sau đó nó sẽ cung cấp cho bạn ađường dẫn. Nhưng nếu bạn viết một tập lệnh bvới source anó và chạy ./b, nó sẽ trả về bđường dẫn của nó.
PSkocik

12

Những câu trả lời này đúng cho các trường hợp họ nêu nhưng vẫn có vấn đề nếu bạn chạy tập lệnh từ tập lệnh khác bằng từ khóa 'nguồn' (để nó chạy trong cùng một vỏ). Trong trường hợp này, bạn nhận được $ 0 của tập lệnh gọi. Và trong trường hợp này, tôi không nghĩ có thể lấy tên của kịch bản.

Đây là một trường hợp cạnh và không nên được thực hiện nghiêm túc. Nếu bạn chạy tập lệnh từ tập lệnh khác trực tiếp (không có 'nguồn'), sử dụng $ 0 sẽ hoạt động.


2
Bạn có một điểm rất tốt. Không phải là một trường hợp cạnh IMO. Mặc dù có một giải pháp: Xem câu trả lời của Dimitre Radoulov ở trên
Serge Wautier

10

Vì một số ý kiến ​​hỏi về tên tệp mà không có phần mở rộng, đây là một ví dụ về cách thực hiện điều đó:

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

Thưởng thức!


9

Re: Câu trả lời (được chấp nhận) của Tanktalus ở trên, một cách gọn gàng hơn là sử dụng:

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

Nếu tập lệnh của bạn đã được lấy từ tập lệnh bash khác, bạn có thể sử dụng:

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

Tôi đồng ý rằng sẽ gây nhầm lẫn cho các liên kết tượng trưng nếu mục tiêu của bạn là cung cấp phản hồi cho người dùng, nhưng có những lúc bạn cần phải đặt tên chuẩn cho tập lệnh hoặc tập tin khác, và đây là cách tốt nhất, imo.


1
Cảm ơn cho sourcetên tập lệnh d.
Fredrick Gauss

8

Bạn có thể sử dụng $ 0 để xác định tên tập lệnh của mình (với đường dẫn đầy đủ) - để chỉ lấy tên tập lệnh, bạn có thể cắt biến đó bằng

basename $0

8

nếu tập lệnh shell của bạn như

/home/mike/runme.sh

$ 0 là tên đầy đủ

 /home/mike/runme.sh

tên cơ sở $ 0 sẽ lấy tên tệp cơ sở

 runme.sh

và bạn cần đặt tên cơ bản này vào một biến như

filename=$(basename $0)

và thêm văn bản bổ sung của bạn

echo "You are running $filename"

vì vậy các kịch bản của bạn như

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"

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

Điều này giải quyết các liên kết tượng trưng (realpath làm điều đó), xử lý khoảng trắng (dấu ngoặc kép làm điều này) và sẽ tìm thấy tên tập lệnh hiện tại ngay cả khi có nguồn gốc (./myscript) hoặc được gọi bởi các tập lệnh khác ($ BASH_SOURCE xử lý). Sau tất cả điều đó, thật tốt khi lưu cái này trong một biến môi trường để sử dụng lại hoặc để sao chép dễ dàng ở nơi khác (this =) ...


3
FYI realpathkhông phải là lệnh BASH tích hợp. Đó là một tệp thực thi độc lập chỉ có sẵn trong một số bản phân phối nhất định
StvnW

4

Trong bashbạn có thể nhận được tên tập tin kịch bản bằng cách sử dụng $0. Nói chung $1, $2vv là để truy cập các đối số CLI. Tương tự $0là truy cập vào tên kích hoạt tập lệnh (tên tệp tập lệnh).

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

Nếu bạn gọi tập lệnh với đường dẫn như thế /path/to/script.shthì $0cũng sẽ cho tên tệp bằng đường dẫn. Trong trường hợp đó cần sử dụng $(basename $0)để chỉ lấy tên tệp script.


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

2

$0không trả lời câu hỏi (theo tôi hiểu). Một cuộc biểu tình:

$ script script.sh
#! / thùng / sh
tiếng vang `tên cơ sở $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
liên kết

Làm thế nào để một người có ./linktoscriptthể in ra script.sh?

[EDIT] Per @ephemient trong các bình luận ở trên, mặc dù điều liên kết tượng trưng có vẻ bị chiếm đoạt, có thể hiểu $0rằng nó không đại diện cho tài nguyên hệ thống tập tin. OP có một chút mơ hồ về những gì anh ấy muốn.


đã thêm câu trả lời cho câu trả lời của tôi ở trên, nhưng tôi không đồng ý rằng đây là điều thực sự muốn (hoặc bạn nên muốn nó, loại bỏ một số tình tiết giảm nhẹ mà tôi hiện không thể hiểu được)
Tanktalus

2
Tôi nghĩ rằng in linktoscript thay vì script.sh là một tính năng, không phải là một lỗi. Một số lệnh unix sử dụng tên của họ để thay đổi hành vi của họ. Một ví dụ là vi / ex / view.
mouviciel

@Tanktalus: Có những trường hợp bạn muốn hành vi thay đổi dựa trên vị trí thực của tệp - trong một dự án trước đó tôi có một tập lệnh "Nhánh hoạt động", sẽ liên kết với đường dẫn một số công cụ từ nhánh tôi cải tiến; những công cụ đó sau đó có thể tìm ra thư mục nào họ đã được kiểm tra để chạy với các tệp phù hợp.
Andrew Aylett

2

Thông tin cảm ơn Bill Hernandez. Tôi đã thêm một số ưu tiên tôi đang áp dụng.

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

Chúc mừng


1

Ngắn gọn, rõ ràng và đơn giản, trong my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

echo "You are running '$running_file_name' file."

Đặt ra:

./my_script.sh
You are running 'my_script.sh' file.


0

Đây là những gì tôi nghĩ ra, lấy cảm hứng từ câu trả lời của Dimitre Radoulov (nhân tiện tôi đã nêu lên, nhân tiện) .

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

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

bất kể bạn gọi kịch bản của bạn như thế nào

. path/to/script.sh

hoặc là

./path/to/script.sh


-6

một cái gì đó như thế này?

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, không trả lời câu hỏi (không chỉ ra cách tìm tên tập lệnh) và là một ví dụ khó hiểu trong tập lệnh rất lỗi.
Điểm_Under
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.