Search code examples
bashzsh

IFS in zsh behave differently than bash


foo() {
    local lines=$(ls -l)
    local IFS=$'\n'
    for line in $lines; do
        echo $line
    done
}

In zsh, the loop is only executed once, because the output for ls -l command is not split by new lines and the entire chunk of text is passed into $line. In bash though, works as intended. How can I have exactly same behaviour in both shells?

I'm not looking for an alternative solutions of looping over "ls" output. Just trying to understand why it's not working in zsh but works perfectly fine in bash. Instead of "ls" command can be anything else. e.g. "ps -a". The behaviour is the same. Output is not split by line.

Thanks!


Solution

  • ZSH does not split words by default (like other shells):

    foo() {
        local lines=$(ls -l)
    
        local IFS=$'\n'
        if [ $ZSH_VERSION ]; then
          setopt sh_word_split
        fi
    
        for line in $lines; do
            echo line: $line
        done
    }
    
    foo
    

    Credits go to @chris-johnsen with his answer. I support his argument to better use arrays (where possible):

    local lines=("$(ls -l)")
    

    Other options would be (ZSH only):