zsh vs for循环中的bash差异,什么是zsh方式?

时间:2014-07-15 10:55:12

标签: bash shell zsh

假设有一个简单的脚本。

for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done

并且hadoop classpath的原始输出看起来像这样(查找jar的文件夹列表):

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

如果我在Bourne shell中运行上面的脚本,结果将是这样的(它将给出指定类路径中包含的所有jar的唯一列表):

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
...

然而,在zsh中,我仍然得到:

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

(即只是类路径中的文件夹)。

为了好奇,将目录列表转换为zsh中的文件排序列表的方法是什么?

有问题的系统是Red Hat Enterprise Linux Server 6.5版。

3 个答案:

答案 0 :(得分:8)

与Bourne shell不同,zsh在扩展参数后不执行文件名生成。即$x正是它所说的,变量x的值作为一个参数传递而没有拆分或文件名扩展。要强制进行空格分割,请使用${=x}或调用setopt sh_word_split。要强制进行文件名扩展,请使用${~x}或调用setopt glob_subst。为防止zsh在目录不包含文件时抱怨,请使用(N)修饰符在模式持续时间内设置NULL_GLOB选项。

将所有这些结果合并到:

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

请注意,如果您使用的是GNU排序(Linux上的默认设置),sort | uniq可以缩短为sort -u

答案 1 :(得分:0)

首先将输出存储在变量上:

t=$(hadoop classpath | tr ':' ' ' | sort | uniq)

$()比反引号更受欢迎。)

然后按user4815162342

的建议运行${~var}格式的循环
for file in ${~t}; do
    echo "$file"
done

答案 2 :(得分:0)

a" zsh-native"相当于

$(hadoop classpath | tr ':' '\n' | sort | uniq)

${(s/:/ou)$(hadoop classpath)}

整个事情是参数扩展${...}:您可以使用参数扩展而不仅仅是参数名称,以使用参数扩展标志而无需命名您要处理的值。在这种情况下,我们将一些参数标记应用于$(hadoop classpath)的结果,它们是:

  • s/:/ - 将扩展分为:分隔符上的多个单词
  • o - 按升序对结果字词进行排序
  • u - 仅展开每个唯一字词的第一个匹配项

然后你遇到GLOB_SUBST的问题:默认情况下,zsh不对参数扩展执行通配。您可以使用setopt GLOB_SUBST全局更改此设置,或使用${~spec}表单进行特定扩展:

for f in ${(s/:/ou)$(hadoop classpath)}; do echo $~f; done

或者,您可以对要循环的扩展启用GLOB_SUBST

for f in ${(s/:/ou)~$(hadoop classpath)}; do echo $f; done

并意识到整个循环是多余的:

print -l ${(s/:/ou)~$(hadoop classpath)}

还有一件事:我怀疑你只使用sort作为uniq的拐杖。您不需要o参数扩展标记来实现每个路径名的一个副本,u将无需排序即可应对。