Khởi tạo các biến Bash - Có bắt buộc, được đề xuất hoặc xác định khi bạn đi không


9

Có bất kỳ lợi thế / bất lợi nào của việc khởi tạo giá trị của biến bash trong tập lệnh, trước mã chính hoặc biến cục bộ trong hàm trước khi gán giá trị thực cho nó không?

Tôi có cần phải làm một cái gì đó như thế này:

init()
{
    name=""
    name=$1
}

init "Mark"

Có bất kỳ rủi ro nào về các biến được khởi tạo với các giá trị rác (nếu không được khởi tạo) và có ảnh hưởng tiêu cực đến các giá trị của các biến không?


2
Bạn lấy ý tưởng này ở đâu?

6
@DoritoStyle Vâng, nếu một ngôn ngữ được sử dụng cho các ngôn ngữ cấp thấp hơn, chẳng hạn như C, thì đây là một điều hoàn toàn hợp lệ cần được quan tâm.
Kusalananda

@Kusalananda Không phải là C tương đương với mã đó name = ""; name = argv[1];sao? Và đó không phải là vô nghĩa sao?
Joseph Sible-Phục hồi Monica

1
@ JosephSible-RebstateMonica Có. Mã C mà bạn đã đăng là vô nghĩa. Tuy nhiên, trong C, trái ngược với trong trình bao, các biến chưa được khởi tạo không có giá trị được xác định rõ. Điều này có nghĩa là việc khởi tạo các biến trong các ngôn ngữ như C có ý nghĩa trong nhiều trường hợp. Làm như vậy trong vỏ là không cần thiết. Chỉ khởi tạo một biến trong C để ngay lập tức đặt nó thành một giá trị khác là vô nghĩa, như bạn chỉ ra.
Kusalananda

Câu trả lời:


22

Không có lợi khi gán một chuỗi rỗng cho một biến và sau đó ngay lập tức gán một chuỗi biến khác cho nó. Việc gán giá trị cho biến shell sẽ ghi đè hoàn toàn giá trị trước đó.

Theo hiểu biết của tôi, không có khuyến nghị nào nói rằng bạn nên khởi tạo rõ ràng các biến thành các chuỗi trống. Trong thực tế, làm như vậy có thể che dấu lỗi trong một số trường hợp (lỗi có thể rõ ràng hơn nếu chạy bên dưới set -u, xem bên dưới).

Một biến unset, không được sử dụng kể từ khi bắt đầu tập lệnh hoặc không được đặt rõ ràng bằng cách chạy unsetlệnh trên nó, sẽ không có giá trị. Giá trị của một biến như vậy sẽ không có gì. Nếu được sử dụng như "$myvariable", bạn sẽ nhận được tương đương ""và bạn sẽ không bao giờ nhận được "dữ liệu rác".

Nếu tùy chọn vỏ nounsetđược thiết lập với một trong hai set -o nounsethoặc set -u, sau đó tham khảo một biến unset sẽ gây ra vỏ để tạo ra một lỗi (và một vỏ không tương tác sẽ chấm dứt):

$ set -u
$ echo "$myvariable"
/bin/sh: myvariable: parameter not set

hoặc, trong bash:

$ set -u
$ echo "$myvariable"
bash: myvariable: unbound variable

Các biến Shell sẽ được khởi tạo bởi môi trường nếu tên của biến tương ứng với một biến môi trường hiện có.

Nếu bạn mong đợi rằng bạn đang sử dụng một biến có thể được khởi tạo bởi môi trường theo cách này (và nếu không mong muốn), thì bạn có thể bỏ đặt nó một cách rõ ràng trước phần chính của tập lệnh của bạn:

unset myvariable    # unset so that it doesn't inherit a value from the environment

cái này cũng sẽ loại bỏ nó như một biến môi trường, hoặc, bạn có thể đơn giản bỏ qua giá trị ban đầu của nó và chỉ ghi đè lên nó bằng một phép gán (điều này cũng làm cho biến môi trường thay đổi giá trị).

Bạn sẽ không bao giờ gặp phải rác chưa được khởi tạo trong một biến shell (trừ khi, như đã nêu, rác đó đã tồn tại trong một biến môi trường có cùng tên).


3
Mặc dù không có giá trị để đặt biến thành giá trị trống và sau đó ngay lập tức đặt giá trị đó như OP theo nghĩa đen, nhưng có giá trị trong việc đặt giá trị trống (hoặc unsetting) trước khi chạy một loại forhoặc whilevòng lặp để đặt nó thành một tính toán giá trị nếu có bất kỳ cơ hội nào mà vòng lặp sẽ không thực sự chạy do (các) điều kiện không được đáp ứng; và biến có thể đã được đặt thành một số giá trị khác trong môi trường tập lệnh được kế thừa. Nhưng tốt hơn hết là đặt mọi thứ vào một main()và xác định các biến là local.
Monty Harder

Bạn cũng có thể phát hiện các biến unset sử dụng các nhà khai thác mở rộng tham số, bởi bỏ qua :, ví dụ như${myvariable-defaultvalue}
Barmar

3

Chỉnh sửa : Rất tiếc, khai báo rõ ràng khác với khởi tạo. Dù sao tôi cũng sẽ để nó ở đây để những lập trình viên mới làm quen như tôi có thể học hỏi từ sai lầm của mình.


Ưu điểm của việc khai báo các biến cục bộ trong một hàm là bạn có thể dễ dàng sao chép mã.

Ví dụ: giả sử tôi có một chức năng:

foo(){
    local name
    name="$1"
    echo "$name"
}

Nếu tôi muốn biến nó thành một kịch bản, tôi chỉ cần bỏ qua localcâu lệnh và sao chép mọi thứ khác:

#!/bin/bash
name="$1"
echo "$name"

Nếu khai báo và chuyển nhượng nằm trong cùng một dòng, tôi phải chỉnh sửa localphần đó theo cách thủ công trước khi tôi có thể biến nó thành tập lệnh:

foo(){
    local name="$1"
    echo "$name"
}

Trong ví dụ này không phải là vấn đề lớn, nhưng nếu bạn đang xử lý các chức năng lớn hơn, phức tạp hơn, nó có thể gây đau đớn hơn.


1
Câu hỏi không phải là về việc kết hợp khai báo và khởi tạo. Đó là về việc có nên khởi tạo với một giá trị trống hay không trước khi gán giá trị thực.
Barmar

@Barmar ơi, tôi vừa tra cứu "khởi tạo". Tôi nghĩ rằng nó đồng nghĩa với "tuyên bố" ...
wjandrea
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.