Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Is there a less laborious way for setting a local version of the $path array than what's shown in the following snippet?

foo () {
    local holdpath
    holdpath=($path)
    local path
    path=($holdpath)
    if ( some_condition ) path=( $PREFIX $path )

    # do stuff
}

I'm referring specifically to the song-and-dance with holdpath...

If instead I define

foo () {
    local path=($path)
    if ( some_condition ) path=( $PREFIX $path )

    # do stuff
}

...the first assignment to path triggers a bad pattern error. Of course, if instead I define

foo () {
    local path
    path=($path)
    if ( some_condition ) path=( $PREFIX $path )

    # do stuff
}

...the first assignment to path makes no difference (i.e. it can be omitted without altering the results); with or without it, $path will be empty.

EDIT:

The following script tests the various suggestions I've gotten so far:

foo_0 () {
  echo ${#path}
  local PATH=$PATH
  echo ${#path}
}

foo_1 () {
  echo ${#path}
  eval "local path; path=(${(q)path})"
  echo ${#path}
}

foo_2 () {
  echo ${#path}
  eval "$(local -p path)"
  echo ${#path}
}

for i ( 0 1 2 ) {
  fn=foo_$i
  echo "# $fn"
  $fn
  echo
}

The output is:

# foo_0
22
22

# foo_1
22
1

# foo_2
22
22

So the outputs for foo_0 and foo_2 are at least consistent with what I'm trying to achieve. Something is not working with foo_1 as given above, but I get rid of the (q) in the assignment to path, i.e.

foo_1 () {
  echo ${#path}
  eval "local path; path=($path)"
  echo ${#path}
}

...then the output agrees with that of foo_0 and foo_2. Unfortunately, even after reading the documentation for the q qualifier a couple of times, I don't quite understand what it's supposed to be doing in the original recipe.

Also, I cannot understand why the following command-line variant of foo_0 differs from the one above:

% (foo_0a () { echo ${#path}; local PATH=$PATH; echo ${#path} }; foo_0a)
22
1

FWIW, the corresponding command-line variants of foo_1 and foo_2 produce the same results as their originals in the script:

% (foo_1a () { echo ${#path}; eval "local path; path=(${(q)path})"; echo ${#path} }; foo_1a)
22
1
% (foo_2a () { echo ${#path}; eval "$(local -p path)"; echo ${#path}; }; foo_2a)
22
22

Also, in all the cases above in which echo ${#path} produces 1 instead of 22 the reason is that the local $path variable contains all the individual paths in a single string, separated by spaces.

share|improve this question
1  
A note of warning: use holdpath=("$path[@]"). holdpath=($path) removes the empty elements. –  Stéphane Chazelas Jun 26 '13 at 18:36
    
++Thanks for the pointer, and for editing the many typos in my original post! –  kjo Jun 26 '13 at 19:39
    
I can't reproduce your experience with foo_0a; however, in the process of trying, I noticed that if you have a path component which includes a :, then setting PATH=$PATH increases the size of $path, as I suppose you might expect. So my solution is not correct in that case. But I can't explain your result either. –  rici Jun 27 '13 at 4:23

2 Answers 2

up vote 3 down vote accepted

While for tied arrays, you can use rici's answer, in the general case, you could do:

foo() {
  eval "local array; array=(${(q)array[@]})"
  ...
}

The (q) is to quote the elements of the arrays. For instance, for a value of $PATH like /foo bar:/x$y, "${(q)path[@]}" would expand to /foo\ bar /x\$y. We need to escape those space and dollar characters because that string is passed to eval.

You could also do:

foo() {
  eval "$(local -p array)"
  ...
}

which would work with any type of variable, but that forks an extra process.

share|improve this answer
    
Thanks! I ran into some problems with your first suggestion; please the latest EDIT to my post. –  kjo Jun 26 '13 at 20:21
1  
@kjo, yes of course, sorry, because ${(q)path} was inside double quotes, that quotes a single argument. My last edit should fix it. –  Stéphane Chazelas Jun 26 '13 at 21:31
1  
Not sure if that really counts as less convoluted! –  Gilles Jun 26 '13 at 23:43

Use the scalar PATH form instead of the array path. Making either of these local effectively makes both of them local, so:

foo() {
  local PATH=$PATH
  if ( some_condition ) path=( $PREFIX $path )

  # do stuff
}

(Note that this will not work if some path component has an embedded :.)

Unfortunately, it is not possible to initialize a local array parameter in one statement, making it impossible to initialize a local array parameter using its original value.

share|improve this answer
    
Well, if elements of $path have colons, it's not the only thing that won't work since $PATH cannot contain a directory name with colon: zsh will be able to search in there, but not the other tools, and for instance, if you do path+=(~/:::) to add your weirdly named bin directory, zsh will find executables in there but other tools (think env, xargs...) will look for them in the current directory (3 times) which is dangerous. –  Stéphane Chazelas Jun 27 '13 at 9:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.