Cakupan Variabel Lokal dalam Fungsi Shell


28

Setelah membaca 24.2. Variabel Lokal , saya berpikir bahwa mendeklarasikan variabel vardengan kata kunci localberarti bahwa varnilai hanya dapat diakses dalam blok kode yang dibatasi oleh kurung kurawal suatu fungsi.

Namun, setelah menjalankan contoh berikut, saya menemukan bahwa varjuga dapat diakses, dibaca dan ditulis dari fungsi dipanggil oleh blok kode - yaitu meskipun vardinyatakan localuntuk outerFunc, innerFuncmasih bisa membacanya dan mengubah nilainya.

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}]"

Keluaran:

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:]

T: Apakah itu bug di shell saya (bash 4.3.42, Ubuntu 16.04, 64bit) atau apakah itu perilaku yang diharapkan?

EDIT: Dipecahkan. Seperti dicatat oleh @MarkPlotnick, ini memang perilaku yang diharapkan.


Ini adalah perilaku yang diharapkan
fpmurphy

2
Apakah saya satu-satunya yang berpikir itu aneh bahwa pada baris terakhir output nilai varkosong? vardiatur secara global innerFunc, jadi mengapa tidak tetap sampai akhir skrip?
Harold Fischer

Jawaban:


22

Variabel Shell memiliki cakupan dinamis . Jika variabel dideklarasikan sebagai lokal ke suatu fungsi, cakupan itu tetap sampai fungsi kembali.

Ada dua pengecualian:

  1. di ksh93, jika suatu fungsi didefinisikan dengan function_name () { … }sintaks standar , maka variabel lokalnya mematuhi pelingkupan dinamis. Tetapi jika suatu fungsi didefinisikan dengan sintaks ksh function function_name { … }maka variabel lokalnya mematuhi scoping lexical / static, sehingga mereka tidak terlihat dalam fungsi lain yang disebut oleh ini.

  2. itu zsh/private Plugin autoloadable di zshmenyediakan dengan privatekata kunci / builtin yang dapat digunakan untuk mendeklarasikan variabel dengan lingkup statis.

abu, bash, pdksh, dan turunannya, namun hanya memiliki pelingkupan dinamis.


Apakah semua variabel dalam shell memiliki cakupan dinamis atau apakah ini hanya berlaku untuk variabel yang dideklarasikan local?
Harold Fischer

@ HaroldFischer Semua variabel memiliki cakupan dinamis. Dengan typesetatau declareatau localdeklarasi, cakupannya sampai fungsi kembali. Tanpa pernyataan seperti itu, ruang lingkupnya bersifat global.
Gilles 'SANGAT berhenti menjadi jahat'

6

Ini bukan bug, panggilan di dalam konteks outerFunc menggunakan salinan lokal $ var. "Lokal" di outerFunc berarti global tidak berubah. Jika Anda memanggil innerFunc di luar outerFunc, maka akan ada perubahan ke $ var global, tetapi bukan $ var lokal outerFunc. Jika Anda menambahkan "lokal" ke innerFunc, maka $ var outerFunc tidak akan berubah - pada dasarnya, akan ada 3 di antaranya:

  • $ global :: var
  • $ outerFunc :: var
  • $ innerFunc :: var

untuk menggunakan format namespace Perl, semacam.


2

Anda dapat menggunakan fungsi untuk memaksa lingkup lokal:

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

Contoh:

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"

Hasil:

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]

Sumber


2

Di function innerFunc()dalam var='new value'tidak dinyatakan sebagai lokal , oleh karena itu tersedia dalam lingkup terlihat (sekali fungsi telah dipanggil).

Sebaliknya, di function outerFunc()dalam local var='initial value'dinyatakan sebagai lokal , oleh karena itu tidak tersedia di lingkup global (bahkan jika fungsi telah dipanggil).

Karena innerFunc()dipanggil sebagai anak kecil outerFunc(), var berada dalam lingkup lokal outerFunc().

man 1 bash dapat membantu memperjelas

[opsi] lokal [nama [= nilai] ...]

Untuk setiap argumen, nama variabel lokal bernama dibuat, dan nilai yang diberikan. Opsi dapat berupa opsi yang diterima dengan menyatakan. Ketika lokal digunakan dalam suatu fungsi, itu menyebabkan nama variabel memiliki ruang lingkup yang terlihat terbatas pada fungsi itu dan anak-anaknya. ...

Perilaku tersirat yang diharapkan dalam deskripsi dapat dicapai dengan menyatakan local var='new valuedifunction innerFunc() .

Seperti yang dinyatakan orang lain, ini bukan bug di shell bash. Semuanya berfungsi sebagaimana mestinya.


Pernyataan pertama Anda bertentangan dengan apa yang dilihat pengguna. Mencetak nilai vardalam lingkup global, setelah menelepon innerFuncmelalui outFunc, tidak mencetak new value.
Kusalananda
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.