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?
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?
Câu trả lời:
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.sh
hơn là foo.sh
.
$0
trong 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.
# ------------- 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 ------------- #
show_params
, tức là tên mà không có bất kỳ phần mở rộng tùy chọn?
${0##*/}
. Đã thử nghiệm bằng GitBash.
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"
dirname $BASE_SOURCE
" để lấy thư mục chứa các tập lệnh.
$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)
. <filename> [arguments]
, $0
sẽ đặt tên của vỏ người gọi. Ít nhất là trên OSX chắc chắn.
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.
Nếu bạn muốn nó mà không có đường dẫn thì bạn sẽ sử dụng ${0##*/}
Để 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 đó).
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_SOURCE
biế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]
a
(giả sử a
nộ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 b
với source a
nó và chạy ./b
, nó sẽ trả về b
đường dẫn của nó.
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.
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!
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.
source
tên tập lệnh d.
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"
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 =) ...
realpath
khô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
Trong bash
bạ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
, $2
vv là để 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 với đường dẫn như thế /path/to/script.sh
thì $0
cũ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.
$0
khô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ó ./linktoscript
thể 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 $0
rằ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ô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
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.
DIRECTORY=$(cd `dirname $0` && pwd)
Tôi đã nhận được những điều 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 nó được lưu trữ không? , nhưng tôi nghĩ nó cũng hữu ích cho chủ đề này.
Đâ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
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