zsh与bash在for循环方面的区别,zsh的方法是什么?

3
假设有一段简单的脚本。
for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done

原始的 hadoop classpath 输出结果看起来像这样(即要查找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

如果我在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版本。


2
一般来说,在bash中这样使用for循环是不被赞同的(尽管由于您明确需要字符串拆分和全局扩展,您的用例可能是一个例外);请参见http://mywiki.wooledge.org/DontReadLinesWithFor。 - Charles Duffy
1
你有没有注意到整个 | sort | uniq 的操作其实是无用的,因为 sort 命令只会对行进行排序,而 tr 命令的输出仍然在单行中。如果你想要对 hadoop classpath 输出进行排序,那么管道命令应该是 hadoop classpath | tr ':' '\n' | sort | uniq - just somebody
3个回答

8
与 Bourne Shell 不同,zsh 在扩展参数后不执行文件名生成。即 $x 意味着它所说的正是变量 x 的值,作为一个参数传递而不进行分割或文件名扩展。要强制进行空格分割,请使用 ${=x} 或调用 setopt sh_word_split。要强制进行文件名扩展,请使用 ${~x} 或调用 setopt glob_subst。为了防止 zsh 在目录中没有文件时发出投诉,请使用设置 NULL_GLOB 选项的 (N) 修改符来限制模式的持续时间。将所有这些组合起来得到以下结果:
classpath=$(hadoop classpath | tr ':' ' ' | sort | uniq)
for file in ${=~classpath}(N); do
  echo $file
done

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

感谢这些好的建议,虽然还不完全一样:这个脚本生成的是文件名块的列表,而不是像bash那样的排序文件列表。 - Ashalynd
@Ashalynd 我想我找到了问题所在。我已经修改了答案,现在它会生成单个条目的列表,而不是扩展块的列表。 - user4815162342
很酷,它有效!只有一件小事:如果其中一个文件夹中没有文件(就像在/usr/local/hadoop/contrib/capacity-scheduler/.jar的情况下),那么shell会抱怨:找不到匹配项:/usr/local/hadoop/contrib/capacity-scheduler/.jar并且根本不输出任何内容。因此,理想情况是跳过没有文件的文件夹,但我想我要求太多了 :) - Ashalynd
1
@Ashalynd 噢,是的,我已经修改了答案,使用扩展中的(N)修饰符来解决这个问题。 - user4815162342

0

一个“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可以在不排序的情况下处理。


0

首先将输出存储在一个变量中:

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

($() 优先于反引号。)

然后按照 user4815162342 的建议,使用 ${~var} 格式运行循环。

for file in ${~t}; do
    echo "$file"
done

如果我尝试从shell运行它,zsh会在设置t时的那一行显示“Bad assignment”。如果我尝试从脚本运行它,则会在${~t}部分显示“Bad substitution”。 - Ashalynd
啊,抱歉,我的错。我必须在脚本开头指定zsh。但仍然没有运气,因为它说:“找不到匹配项:/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”。 - Ashalynd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接