tra-loi-cau-hoi-phat-trien-web.com

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

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.sh, thì 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?

490
Ma99uS
me=`basename "$0"`

Để đọc qua một liên kết tượng trưng, ​​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 gọi là (nghĩ gzip và gunzip trên một số nền tảng).

518
Tanktalus
[.__.] # ------------- 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
 [.___ ------------- GỌI ------------- # [.__.] [.__.] # Lưu ý trên dòng tiếp theo, đối số đầu tiên được gọi trong phạm vi gấp đôi, [.__.] # và dấu ngoặc đơn, vì nó chứa hai từ [.__.] [.__.] $ /misc/Shell_scripts/check_root/show_parms.sh "'xin chào' '" 'william' " [.__.] [.__.] # ------------- KẾT QUẢ ------------- # [.__. ] [.__.] # đối số được gọi với ---> 'xin chào' 'william' [.__ > '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 ------------- #
229
Bill Hernandez

Với bash> = 3 các 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"
172
Dimitre Radoulov

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, nên thực hiện tốt việc luôn trích dẫn hai tên biến trong Shell.

60
Josh Lee

$BASH_SOURCE đưa ra câu trả lời chính xác khi tìm nguồn cung cấp tập lệnh.

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

$(basename $BASH_SOURCE) 
58
Zainka

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

21
Mr. Muskrat

Để 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ó theo liên kết hoàn toàn (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 đó).

19
Travis B. Hartwell

Tôi đã tìm thấy dòng này luôn hoạt động, bất kể tệp đ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 tượng trưng, ​​hãy sử dụng readlink trên đường dẫn bạn đi ở trên, đệ quy hoặc không đệ quy.

Lý do hoạt động của một lớp lót được giải thích bằng việc sử dụng biến môi trường BASH_SOURCE 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]

13
gkb0986

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 Shell). 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à trường hợp Edge và không nên thực hiện quá 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.

11
Jim Dodd

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 khác, và đây là cách tốt nhất, imo.

8
simon

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!

7
Simon Mattes

nếu lệnh gọi 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"
6
LawrenceLi
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ý điều đó). 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 =) ...

5
jcalfee314

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
5
VolkA

Trong bash bạn có thể lấy tên tệp tập lệnh bằng cách sử dụng $0. Nói chung $1, $2, v.v. để truy cập các đối số CLI. Tương tự $0 là 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 có đường dẫn như /path/to/script.sh thì $0 cũng sẽ cung cấp tên tệp có đường dẫn. Trong trường hợp đó, cần sử dụng $(basename $0) để chỉ nhận tên tệp tập lệnh.

2
rashok

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

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

Đây là những gì tôi nghĩ ra, lấy cảm hứng từ Dimitre Radoulov 's câu trả lời (mà 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
0
Salathiel Genèse

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

Ra đặt:

./my_script.sh
You are running 'my_script.sh' file.
0
Bơ Loong A Nhứi

tiếng vang "Bạn đang chạy $ 0"

0
mmacaulay

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

 $ kịch bản mèo.sh 
 #! /bin/sh
echo `tên cơ sở $ 0` 
 $ ./script.sh 
 script.sh 
 $ ln script.sh linktoscript 
 $ ./linktoscript 
 linktoscript 

Làm thế nào để một người nhận được ./linktoscript để in ra script.sh?

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

0
Chris Conway
DIRECTORY=$(cd `dirname $0` && pwd)

Tôi đã nhận được câu hỏi trên từ một câu hỏi Stack Overflow khác, Tập lệnh Bash có thể cho biết thư mục nào được lưu trong thư mục không?, nhưng tôi nghĩ nó cũng hữu ích cho chủ đề này.

0
Koter84