假设有一个简单的脚本。
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版。
答案 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)
($()
比反引号更受欢迎。)
${~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
将无需排序即可应对。