zsh vs bash difference in for loop, what is the zsh way?

bash, shell, zsh

Solution

Unlike Bourne shell, zsh does not perform filename generation after expanding a parameter. I.e. `$x` means exactly what it says, the value of the variable `x` passed on as one argument without splitting or filename expansion. To force whitespace splitting, use `${=x}` or invoke `setopt sh_word_split`. To force filename expansion, use `${~x}` or invoke `setopt glob_subst`. To prevent zsh from complaining when a directory contains no files, use the `(N)` modifier that sets the `NULL_GLOB` option for the duration of the pattern.

Combining all these results in:

classpath=$(hadoop classpath | tr ':' ' ' | sort | uniq)
for file in ${=~classpath}(N); do
  echo $file
done

Note that `sort | uniq` can be shortened to `sort -u` if you're using GNU sort (the default on Linux).

Problem

Suppose there is a simple script. ``` for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done ``` and the original output of `hadoop classpath` looks like this (the list of folders where to look for the jars): ``` zsh %> hadoop classpath /usr/local/hadoop/etc/hadoop:/usr/local/hadoop/share/hadoop/common/lib/*:/usr/local/hadoop/share/hadoop/common/*:/usr/local/hadoop/share/hadoop/hdfs:/usr/local/hadoop/share/hadoop/hdfs/lib/*:/usr/local/hadoop/share/hadoop/hdfs/*:/usr/local/hadoop/share/hadoop/yarn/lib/*:/usr/local/hadoop/share/hadoop/yarn/*:/usr/local/hadoop/share/hadoop/mapreduce/lib/*:/usr/local/hadoop/share/hadoop/mapreduce/*:/usr/local/hadoop/contrib/capacity-scheduler/*.jar ``` If I run the above script in Bourne shell, the results will be like this (which will give the unique list of all jars contained in the specified classpath): ``` bash-4.1$ for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done /usr/local/hadoop/etc/hadoop /usr/local/hadoop/share/hadoop/common/lib/activation-1.1.jar /usr/local/hadoop/share/hadoop/common/lib/asm-3.2.jar /usr/local/hadoop/share/hadoop/common/lib/avro-1.7.4.jar /usr/local/hadoop/share/hadoop/common/lib/commons-beanutils-1.7.0.jar /usr/local/hadoop/share/hadoop/common/lib/commons-beanutils-core-1.8.0.jar /usr/local/hadoop/share/hadoop/common/lib/commons-cli-1.2.jar /usr/local/hadoop/share/hadoop/common/lib/commons-codec-1.4.jar ... ``` In zsh, however, I still get: ``` zsh %> for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done /usr/local/hadoop/etc/hadoop /usr/local/hadoop/share/hadoop/common/lib/* /usr/local/hadoop/share/hadoop/common/* /usr/local/hadoop/share/hadoop/hdfs /usr/local/hadoop/share/hadoop/hdfs/lib/* /usr/local/hadoop/share/hadoop/hdfs/* /usr/local/hadoop/share/hadoop/yarn/lib/* /usr/local/hadoop/share/hadoop/yarn/* /usr/local/hadoop/share/hadoop/mapreduce/lib/* /usr/local/hadoop/share/hadoop/mapreduce/* /usr/local/hadoop/contrib/capacity-scheduler/*.jar ``` (I.e. nothing more than the folders from the classpath). For the sake of curiosity, what would be the way to convert the list of directories into the sorted list of files in zsh? The system in question is Red Hat Enterprise Linux Server release 6.5.

Original source