Phạm vi của các biến cục bộ trong hàm Shell


28

Sau khi đọc 24.2. Các biến cục bộ , tôi nghĩ rằng việc khai báo một biến varbằng từ khóa localcó nghĩa varlà giá trị đó chỉ có thể truy cập được trong khối mã được phân định bởi các dấu ngoặc nhọn của hàm.

Tuy nhiên, sau khi chạy ví dụ sau, tôi phát hiện ra rằng varcũng có thể được truy cập, đọc và viết từ các chức năng gọi bởi rằng khối mã - tức là mặc dù varđược khai báo localđể outerFunc, innerFuncvẫn có thể đọc nó và thay đổi giá trị của nó.

Run It Online

#!/usr/bin/env bash

function innerFunc() {
    var='new value'
    echo "innerFunc:                   [var:${var}]"
}

function outerFunc() {
    local var='initial value'

    echo "outerFunc: before innerFunc: [var:${var}]"
    innerFunc
    echo "outerFunc: after  innerFunc: [var:${var}]"
}

echo "global:    before outerFunc: [var:${var}]"
outerFunc
echo "global:    after  outerFunc: [var:${var}]"

Đầu ra:

global:    before outerFunc: [var:]               # as expected, `var` is not accessible outside of `outerFunc`
outerFunc: before innerFunc: [var:initial value]
innerFunc:                   [var:new value]      # `innerFunc` has access to `var` ??
outerFunc: after  innerFunc: [var:new value]      # the modification of `var` by `innerFunc` is visible to `outerFunc` ??
global:    after  outerFunc: [var:]

H: Có phải đó là một lỗi trong trình bao của tôi (bash 4.3.42, Ubuntu 16.04, 64 bit) hay đó là hành vi dự kiến?

EDIT: Đã giải quyết. Theo ghi nhận của @MarkPlotnick, đây thực sự là hành vi được mong đợi.


Đó là hành vi dự kiến
fpmurphy

2
Tôi có phải là người duy nhất nghĩ rằng điều kỳ lạ là ở dòng đầu ra cuối cùng, giá trị của varnó là trống không? varđược đặt trên toàn cầu innerFunc, vậy tại sao nó không được sử dụng cho đến khi kết thúc tập lệnh?
Harold Fischer

Câu trả lời:


22

Các biến Shell có phạm vi động . Nếu một biến được khai báo là cục bộ của hàm, phạm vi đó sẽ duy trì cho đến khi hàm trả về.

Có hai trường hợp ngoại lệ:

  1. trong ksh93, nếu một hàm được xác định theo function_name () { … }cú pháp chuẩn , thì các biến cục bộ của nó tuân theo phạm vi động. Nhưng nếu một hàm được định nghĩa với cú pháp ksh function function_name { … }thì biến cục bộ của nó tuân theo phạm vi từ vựng / tĩnh, do đó chúng không thể nhìn thấy trong các hàm khác được gọi bởi hàm này.

  2. các zsh/privatePlugin autoloadable trong zshcung cấp với một privatetừ khóa / dựng sẵn mà có thể được sử dụng để khai báo một biến với phạm vi tĩnh.

tro, bash, pdksh và các dẫn xuất, bosh chỉ có phạm vi động.


Có phải tất cả các biến trong shell đều có phạm vi động hay điều này chỉ áp dụng cho các biến được khai báo với local?
Harold Fischer

@HaroldFischer Tất cả các biến có phạm vi động. Với một typesethoặc declarehoặc localkhai báo, phạm vi là cho đến khi hàm trả về. Không có tuyên bố như vậy, phạm vi là toàn cầu.
Gilles 'SO- ngừng trở nên xấu xa'

6

Đó không phải là một lỗi, cuộc gọi bên trong ngữ cảnh của outsFunc sử dụng bản sao $ var cục bộ đó. "Địa phương" trong outsFunc có nghĩa là toàn cầu không thay đổi. Nếu bạn gọi InternalFunc bên ngoài outsFunc, thì sẽ có một thay đổi đối với var var toàn cầu, nhưng không phải là var var cục bộ của outsFunc. Nếu bạn đã thêm "cục bộ" vào InternalFunc, thì $ var của outsFunc sẽ không bị thay đổi - về bản chất, sẽ có 3 trong số chúng:

  • $ toàn cầu :: var
  • $ outsFunc :: var
  • $ InternalFunc :: var

để sử dụng định dạng không gian tên của Perl, sắp xếp.


2

Bạn có thể sử dụng một hàm để buộc phạm vi cục bộ:

sh_local() {
  eval "$(set)" command eval '\"\$@\"'
}

Thí dụ:

x() {
  z='new value'
  printf 'function x, z = [%s]\n' "$z"
}
y() {
  z='initial value'
  printf 'function y before x, z = [%s]\n' "$z"
  sh_local x
  printf 'function y after x, z = [%s]\n' "$z"
}
printf 'global before y, z = [%s]\n' "$z"
y
printf 'global after y, z = [%s]\n' "$z"

Kết quả:

global before y, z = []
function y before x, z = [initial value]
function x, z = [new value]
function y after x, z = [initial value]
global after y, z = [initial value]

Nguồn


2

Trong function innerFunc()trường hợp var='new value'không được khai báo là cục bộ , do đó, nó có sẵn trong phạm vi có thể nhìn thấy (một khi chức năng đã được gọi).

Ngược lại, trong function outerFunc()phần local var='initial value'được khai báo là cục bộ , do đó, nó không có sẵn trong phạm vi toàn cầu (ngay cả khi hàm được gọi).

Bởi vì innerFunc()được gọi là con của outerFunc()var, var nằm trong phạm vi cục bộ của outerFunc().

man 1 bash có thể giúp làm rõ

cục bộ [tùy chọn] [tên [= value] ...]

Đối với mỗi đối số, một tên cục bộ có tên được tạo và giá trị được gán. Tùy chọn có thể là bất kỳ tùy chọn nào được chấp nhận bằng cách khai báo. Khi cục bộ được sử dụng trong một hàm, nó làm cho tên biến có phạm vi nhìn thấy được giới hạn ở hàm đó và các phần tử con của nó. ...

Các hành vi ám chỉ đó là dự kiến trong mô tả có thể đạt được bằng cách tuyên bố local var='new valuetrong function innerFunc().

Như những người khác đã tuyên bố, đây không phải là một lỗi trong bash shell. Mọi thứ đều hoạt động như bình thường.


Tuyên bố đầu tiên của bạn mâu thuẫn với những gì người dùng đang nhìn thấy. In giá trị vartrong phạm vi toàn cầu, sau khi gọi innerFuncqua outFunc, không in new value.
Kusalananda
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.