If we insist on doing things The Wrong Way™
#compdef vc
declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0
Yuck! This of course will break should a filename contain a newline, as (f)
splits on those. Parsing ls
is anyways a bad idea; ZSH can glob files directly into an array:
% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*)
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}
na go'i
go'i
%
But we probably don't need a local array; _files
can complete on a glob:
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
This returns fully qualified file paths; if only the bare filename is required from a search directory we can instead use:
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0
Variables are always available to sub-processes. In:
a=1
(echo "$a")
you see 1.
I think what you meant is that you want the variable to have a local scope and be exported to the environment so that they are passed as environment variables to commands that are executed. The execution of a command is the thing that wipes the memory of a process (and the environment is a way to preserve some data across it), forking a child copies the entire memory so everything is preserved.
For that, you can use local -x
:
a=(1 2)
f() {
local -x a=3
typeset -p a
printenv a # printenv being *executed*
}
f
typeset -p a
gives:
typeset -x a=3
3
typeset -a a=( 1 2 )
Or you can export
it after having been declared local
:
a=(1 2)
f() {
local a=3
export a
typeset -p a
printenv a # printenv being *executed*
}
f
typeset -p a
Note that you can pass a variable in the environment of a single command without defining it otherwise as a shell variable with:
a=(1 2)
f() {
a=3 printenv a # printenv being *executed*
}
f
typeset -p a
Note that local
originated in the Almquist shell in the late 80s, but works differently from zsh
's. In the Almquist shell (and its descendants like dash and the sh of NetBSD/FreeBSD), local
only affects the scope of a variable and doesn't change the value or attributes of a variable.
zsh's local
works more like ksh93's typeset
in that it declares a brand new variable that is independent from the one in the outer scope.
ksh88, bash and pdksh's local
/typeset
try to do that as well but still inherit some attributes from the variable of the outer scope including the export attribute. That changed in ksh93 though note that ksh93 also switched to static scoping and only implements local scope in functions declared with the function f { ...; }
syntax.
Best Answer
In a programming language with static scoping, such as most other programming languages (like C),
There is a global scope and a local scope for each function. Variables appearing in a function are either private to the function or global.
A function can only access either its local variables or the global variables. It cannot access the variables of another function (even its caller) other than via passing by reference.
In a programming language with dynamic scoping,
a function sees the variables of its caller, and there's a scope for each function of your call tree of the function. Scoping is like Russian dolls where variables are stacked on top of the other.
In that stack of scopes, the global scope is only special in that it is the bottom-most one, but functions don't necessarily see variables in that scope if they have been masked as a local variable by any of the function in their call tree. So there is not one global and one local scope.
It helps to know the history here.
1. ksh93
In
ksh93
, a function, at least one declared with the ksh syntax (function f {...}
), follows static scoping.Variables declared with
typeset
in a function are local variables of the function.would output
global_a
.typeset -i var
in a function changes the type of the localvar
variable, with instantiating it in the function scope if it wasn't already.In
ksh93
, a function declared with the Bourne syntax (f() {...}
) doesn't do scoping at all. In that regard, the code of the function appears as if embedded in the caller of the function, so any variable appearing in it will have the same scope as in the caller so either global or local to the top-most function declared with the ksh syntax in its call tree.typeset
would declare the variable in that top-most function (or in the global scope if there was no ksh-syntax function in its call tree).For example, since in ksh-syntax functions, all variables are either private or global, to implement our
integer
as inbash
, we'd need to do it either like:That is using the Bourne function syntax that doesn't do scoping at all.
Or using the ksh syntax:
but to be invoked as:
(that is, by using
.
, the code of integer is interpreted in the context of the caller like when you call.
(source
) on a script).Or using the ksh syntax:
Where the variable is passed as a reference with
-n
like you would do in C or most other programming languages.2. All the other Bourne-like shells
All the other Bourne-like shells (including ksh88, ksh93 was a complete rewrite and the change to static scoping was a (perceived at least) pre-requisite for the functionality to be ever included in the POSIX standard) implement dynamic scoping.
A variable declared with
typeset
without-g
in a function has the local scope of the function.For example,
typeset -i var
would declare the variable local (in the current function scope) and set the integer attribute.For example, in the code at the top, they would all output
f_a
. That is,g
does see the local variable off
.For another example, if
f
callsg
that callsh
. Ifh
does not declare thevar
variable local to its scope, it will seeg
's variable or possiblyf
's ifg
hasn't declaredvar
local, or possibly the variable from the bottom-most scope.In all of
bash
,zsh
,yash
,mksh
, one can change the type or value of a variable in a function without making it local to that function usingtypeset -g
. Like with thatinteger
function in the example you quote. But it's done differently depending on the shell.In
mksh
,yash
,zsh
,typeset -g
doesn't affect the variable at the bottom-most (global) scope, but at the scope it currently been defined.For example, while
typeset -i var
would declare the variable local (in the current function scope) and set the integer attribute,typeset -gi var
just does the latter part (without affecting the scope ofvar
).For example, when a function calls my
integer
function above to add the integer attribute to a variable, as in:It does want
integer
to change the attributes of itsmyvar
variable, not the one in the global scope which it has no knowledge of and may not be able to access.In
bash
,typeset -g
affects the instance of the variable in the global (bottom-most) scope. While that's what theg
stands for, it's not useful in shells with dynamic scoping likebash
.For example, in the first example in your question,
1+1
being output shows that the integer attribute has not been added to the variable. It has been added to a variablea
in the global scope, but not the one that thef
function has access to.