Làm thế nào để truy cập các đối số dòng lệnh của trình gọi bên trong một hàm?


95

Tôi đang cố gắng viết một hàm trong bash sẽ truy cập các đối số dòng lệnh script, nhưng chúng được thay thế bằng các đối số vị trí cho hàm. Có cách nào để hàm truy cập các đối số dòng lệnh nếu chúng không được chuyển vào một cách rõ ràng không?

# Demo function
function stuff {
  echo $0 $*
}

# Echo's the name of the script, but no command line arguments
stuff

# Echo's everything I want, but trying to avoid
stuff $*

3
Tôi hơi bối rối, bạn muốn args bỏ qua chúng?
Ravi Vyas

4
Có, vấn đề là lấy quá các đối số dòng lệnh từ bên trong một hàm mà không chuyển chúng vào dưới dạng đối số hàm. Nó liên quan đến một tình huống xử lý lỗi trong đó tôi muốn xử lý lỗi dựa trên các đối số dòng lệnh độc lập với các đối số được truyền vào hàm.
DonGar

FYI, $*cực kỳ lỗi - nó sẽ thay đổi ./yourScript "first argument" "second argument"thành ./yourscript "first" "argument" "second" "argument"hoặc thay đổi ./yourscript '*.txt'thành một cái gì đó giống như ./yourscript one.txt two.txtbất chấp dấu ngoặc kép.
Charles Duffy

Câu trả lời:


46

Việc tôi đọc sách hướng dẫn tham chiếu bash cho biết nội dung này được ghi lại trong BASH_ARGV, mặc dù nó nói về "ngăn xếp" rất nhiều.

#!/bin/bash

function argv {
    for a in ${BASH_ARGV[*]} ; do
      echo -n "$a "
    done
    echo
}

function f {
    echo f $1 $2 $3
    echo -n f ; argv
}

function g {
    echo g $1 $2 $3
    echo -n g; argv
    f
}

f boo bar baz
g goo gar gaz

Lưu trong f.sh

$ ./f.sh arg0 arg1 arg2
f boo bar baz
farg2 arg1 arg0 
g goo gar gaz
garg2 arg1 arg0 
f
farg2 arg1 arg0 

5
Xin lưu ý rằng việc đặt tên mảng như vậy sẽ khiến các args có thứ tự ngược lại so với dòng lệnh.
Andrew Backer,

93

Nếu bạn muốn có kiểu đối số C (mảng đối số + số đối số), bạn có thể sử dụng $@$#.

$#cung cấp cho bạn số lượng đối số.
$@cung cấp cho bạn tất cả các đối số. Bạn có thể biến điều này thành một mảng bằng cách args=("$@").

Ví dụ:

args=("$@")
echo $# arguments passed
echo ${args[0]} ${args[1]} ${args[2]}

Lưu ý rằng đây ${args[0]}thực sự là đối số thứ nhất chứ không phải tên của tập lệnh của bạn.


5
Điều này không giải quyết câu hỏi - nó hỏi về việc chuyển các đối số dòng lệnh cùng với một hàm shell.
Cascabel

6
@Jefromi, thực sự, nó trả lời câu hỏi một cách hoàn hảo. Bạn có thể sử dụng argsmảng từ bên trong một hàm, nếu bạn khởi tạo nó trước như được mô tả.
vadipp

1
Tôi thấy điều này sạch hơn nhiều so với việc lặp lại các args.
Félix Gagnon-Grenier

1
Điều này là đơn giản và dễ dàng. Câu trả lời chính xác. Bạn đăng này hơn 7 năm trước đây, vì vậy hello từ tương lai: Tháng 7 năm 2017
SDsolar

Thậm chí tốt hơn sẽ là trích dẫn như echo "${args[0]} ${args[1]} ${args[2]}", hoặc các đối số tùy thuộc vào việc mở rộng tên tệp.
Benjamin W.

17
#!/usr/bin/env bash

echo name of script is $0
echo first argument is $1
echo second argument is $2
echo seventeenth argument is $17
echo number of arguments is $#

Chỉnh sửa: vui lòng xem bình luận của tôi về câu hỏi


16

Nhận xét của Ravi về cơ bản là câu trả lời. Các hàm có đối số của riêng chúng. Nếu bạn muốn chúng giống với các đối số dòng lệnh, bạn phải chuyển chúng vào. Nếu không, rõ ràng bạn đang gọi một hàm không có đối số.

Điều đó nói rằng, bạn có thể nếu bạn muốn lưu trữ các đối số dòng lệnh trong một mảng toàn cục để sử dụng trong các hàm khác:

my_function() {
    echo "stored arguments:"
    for arg in "${commandline_args[@]}"; do
        echo "    $arg"
    done
}

commandline_args=("$@")

my_function

Bạn cần phải truy cập vào các đối số dòng lệnh thông qua việc commandline_argsthay đổi, không $@, $1, $2, vv, nhưng chúng có sẵn. Tôi không biết bất kỳ cách nào để gán trực tiếp vào mảng đối số, nhưng nếu ai đó biết cách nào, xin hãy khai sáng cho tôi!

Ngoài ra, hãy lưu ý cách tôi đã sử dụng và trích dẫn $@- đây là cách bạn đảm bảo các ký tự đặc biệt (khoảng trắng) không bị lẫn lộn.


6
# Save the script arguments
SCRIPT_NAME=$0
ARG_1=$1
ARGS_ALL=$*

function stuff {
  # use script args via the variables you saved
  # or the function args via $
  echo $0 $*
} 


# Call the function with arguments
stuff 1 2 3 4

2

Người ta cũng có thể làm như thế này

#!/bin/bash
# script_name function_test.sh
function argument(){
for i in $@;do
    echo $i
done;
}
argument $@

Bây giờ, hãy gọi tập lệnh của bạn như

./function_test.sh argument1 argument2

function print() {là sự pha trộn giữa hai biểu mẫu khai báo hàm khác nhau - function print {, là cú pháp ksh kế thừa mà bash hỗ trợ để tương thích ngược với ksh tiền POSIX (có nghĩa là trước năm 1991) và print() {được chuẩn hóa POSIX. Cân nhắc sử dụng cái này hoặc cái kia để có khả năng tương thích rộng hơn với các shell khác; xem thêm wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

1

Bạn có thể sử dụng từ khóa shift (toán tử?) Để lặp lại chúng. Thí dụ:

#!/bin/bash
function print()
{
    while [ $# -gt 0 ]
    do
        echo $1;
        shift 1;
    done
}
print $*;

2
function print() {là sự pha trộn giữa hai biểu mẫu khai báo hàm khác nhau - function print {, là cú pháp ksh kế thừa mà bash hỗ trợ để tương thích ngược với ksh tiền POSIX (có nghĩa là trước năm 1991) và print() {được chuẩn hóa POSIX. Cân nhắc sử dụng cái này hoặc cái kia để có khả năng tương thích rộng hơn với các shell khác; xem thêm wiki.bash-hackers.org/scripting/obsolete
Charles Duffy

1

Giải pháp của tôi:

Tạo một tập lệnh hàm được gọi sớm hơn tất cả các hàm khác mà không chuyển bất kỳ đối số nào cho nó, như sau:

! / bin / bash

hàm init () {ORIGOPT = "- $ @ -"}

Vì vậy, bạn có thể gọi init và sử dụng var ORIGOPT khi cần thiết, như một điểm cộng, tôi luôn gán một var mới và sao chép nội dung của ORIGOPT trong các chức năng mới của mình, bằng cách đó bạn có thể yên tâm rằng không ai sẽ chạm vào nó hoặc thay đổi nó.

Tôi đã thêm dấu cách và dấu gạch ngang để giúp phân tích cú pháp dễ dàng hơn với 'sed -E'. Ngoài ra, bash sẽ không chuyển nó làm tham chiếu và làm cho ORIGOPT phát triển khi các hàm được gọi với nhiều đối số hơn.

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.