Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I am trying to accomplish something that is easy in bash: look for files in a folder and source them if they exist (and do not output if no files exist).

In bourne shells this is how to do it:

  if [ -d /etc/profile.d ]; then
      for f in `ls -1 /etc/profile.d/*.sh 2> /dev/null`; do
          . $f
      done
  fi

I am new to zsh and cannot get the equivalent working. What am I doing wrong?

if [[ -d "/etc/zsh.d" ]]; then
    for file in (ls -1 /etc/zsh.d/*.zsh 2> /dev/null); do
        source $file
    done
fi

fail: parse error near '>'.

I have tried many variations and cannot get it to be as smooth as the sh/bash equivalent. It's as if redirection does not always work within subshells.

share|improve this question

3 Answers 3

Parsing ls is a bad idea.

Instead you can just use shell globbing to get a list of files:

if [[ -d "/etc/zsh.d" ]]; then
  for f in /etc/zsh.d/*.zsh; do
    source $f
  done
fi

As for the redirection, I'm not sure what the purpose of it is in your example, but if you use this method then it's the shell that might throw an error, not ls, so perform the redirection after done in the for loop.

share|improve this answer
    
The purpose of the redirection is to avoid this error message when no files match. If zsh.d is empty, you're going to see: no matches found: /etc/zsh.d/*.zsh –  chrishiestand Apr 12 '13 at 16:39
    
In my particular case, I will control the contents of the this directory, so parsing ls or find isn't all that bad. But I agree that globbing is preferable. –  chrishiestand Apr 12 '13 at 17:35
    
Can you edit and add "2> /dev/null" after "done" for me? I tried to edit it, but apparently my edit was rejected. With this addition, the answer ideally solves the problem and I will accept it. –  chrishiestand Apr 12 '13 at 21:12
    
If someone rejected an edit they probably deemed it not necessary. In this case, my text below the code says that you can add redirection after done. I can add this as a further example if you like but as you say you're controlling the contents of the directory hopefully you won't get the "no matches" message. –  James O'Gorman Apr 13 '13 at 7:33
    
I unfortunately have no clue what their justification was, however you do not need to convince anyone else if you edit it. And I'm not going to put useless files in there just to prevent an error message - sometimes this directory is supposed to be empty. –  chrishiestand Apr 13 '13 at 8:08

I hope you understand that nearly all sh syntax works just fine in zsh?

# Check dir exists then that there are files in it (silently!)
# If there are no .zsh files in /etc/zsh.d then an empty argument
# is passed to for, which then simply skips the loop

if [ -d /etc/zsh.d ]; then
    for file in $(find /etc/zsh.d/ -name '*.zsh'); do
        source $file
    done
fi

I've tried to make it as fast as possible, since you're in a login script.

This should work for the whole Bourne family.

share|improve this answer
    
This is an excellent workaround for this problem. Where did you get endfor from? On my system I had to change endfor to done and then it works perfectly. –  chrishiestand Apr 12 '13 at 16:50
    
Yes, sorry about that; with BSD Makefiles .endfor is for ending a for loop; I was just careless there. –  Chris Rees Apr 13 '13 at 16:34
up vote 1 down vote accepted

After a later email to the zsh users mailing list, I was given the nearly ideal solution to the problem:

if [[ -d "/etc/zsh.d" ]]; then
  for f in /etc/zsh.d/*.zsh(N); do
    source $f
  done
fi

The (N) tells zsh to set the NULL_GLOB option for that pattern. When no matches are found, the glob expands to an empty string instead of throwing an error. In zsh a for loop over an empty expansion does nothing, which is the behavior we want here.

share|improve this answer
    
If you add NULL_GLOB than you can remove the wrapping if statement. Even if dir doesn't exist you'll get an empty string. So you just need the for loop actually... –  Gilad Peleg Jun 24 at 9:12

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.