zsh与bash在for循环中的区别,zsh的方式是什么?
假设有一个简单的脚本zsh与bash在for循环中的区别,zsh的方式是什么?,bash,shell,zsh,Bash,Shell,Zsh,假设有一个简单的脚本 for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done hadoop classpath的原始输出如下所示(查找jar的文件夹列表): 如果我在BourneShell中运行上述脚本,结果如下(这将给出指定类路径中包含的所有JAR的唯一列表): 然而,在zsh中,我仍然得到: zsh %> for file in `hadoop classpath | tr ':'
for file in `hadoop classpath | tr ':' ' ' | sort | uniq`; do echo $file; done
hadoop classpath的原始输出如下所示(查找jar的文件夹列表):
如果我在BourneShell中运行上述脚本,结果如下(这将给出指定类路径中包含的所有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版。与Bourne shell不同,zsh在扩展参数后不执行文件名生成。也就是说,
$x
的意思正好是它所说的,变量x
的值作为一个参数传递,没有拆分或文件名扩展。要强制拆分空白,请使用${=x}
或调用setopt sh_word_split
。要强制文件名扩展,请使用${~x}
或调用setopt glob_subst
。要防止zsh在目录不包含文件时抱怨,请使用(N)
修饰符,该修饰符在模式持续时间内设置NULL\u GLOB
选项
将所有这些结果合并为:
classpath=$(hadoop classpath | tr ':' ' ' | sort | uniq)
for file in ${=~classpath}(N); do
echo $file
done
请注意,如果使用GNU排序(Linux上的默认值),
sort | uniq
可以缩短为sort-u
。首先将输出存储在变量上:
t=$(hadoop classpath | tr ':' ' ' | sort | uniq)
($()
优于反勾号。)
然后按照建议的${~var}
格式运行循环
相当于
$(hadoop classpath | tr ':' '\n' | sort | uniq)
是
整个过程是一个参数扩展${…}
:您可以在参数名称之外的更多内容上使用参数扩展,以使用参数扩展标志,而无需命名要处理的值。在本例中,我们将一些参数标志应用于$(hadoop类路径)
的结果,它们是:
-在s/:/
分隔符上将扩展名拆分为多个单词:
-按升序对生成的单词进行排序o
-仅展开每个唯一单词的第一个匹配项u
GLOB_SUBST
的问题:默认情况下,zsh不会对参数展开执行globbing。您可以使用setopt GLOB_SUBST
全局更改此值,或使用${~spec}
表单更改特定扩展:
for f in ${(s/:/ou)$(hadoop classpath)}; do echo $~f; done
或者,您可以在正在循环的扩展上启用GLOB\u SUBST
:
for f in ${(s/:/ou)~$(hadoop classpath)}; do echo $f; done
并认识到整个循环是多余的:
print -l ${(s/:/ou)~$(hadoop classpath)}
还有一件事:我怀疑您只是将
sort
用作uniq
的支柱。您不需要使用o
参数扩展标志来实现每个路径名的一个副本,u
将在不排序的情况下处理。感谢您的宝贵提示,尽管它仍然不完全相同:这个脚本生成文件名块列表,而不是像bash那样的文件排序列表。@Ashalynd我想我看到了问题所在。我现在修改了答案,生成了一个单独条目的列表,而不是一个扩展块的列表。太好了,可以了!不过有一件小事:如果其中一个文件夹中碰巧没有文件(比如/usr/local/hadoop/contrib/capacity scheduler/*.jar),那么shell会抱怨:找不到匹配项:/usr/local/hadoop/contrib/capacity scheduler/*.jar,并且什么也不输出。因此,理想的做法是跳过没有文件的文件夹,但我想我要求的太多了:)@Ashalynd啊,是的,我已经修改了答案,使用扩展上的(N)
修饰符来解决这个问题。如果我试图从shell运行它,zsh在设置t时会对行说“错误的赋值”。如果我试着从脚本运行它,它会显示关于${~t}部分的“错误替换”。啊,对不起,我的错误,我必须在脚本的开头指定zsh。尽管如此,没有运气,因为上面写着:找不到匹配项:/usr/local/hadoop/etc/hadoop/usr/local/hadoop/share/hadoop/common/lib/*/usr/local/hadoop/common/*/usr/local/hadoop/share/hdfs/usr/local/hadoop/share/hadoop/hdfs/hdfs/*/usr/local/hadoop/share/hdfs/*/usr/local/hadoop/share/lib/*/usr/local/hadoop/share/hadoop/thread/*/usr/local/hadoop/share/mapreduce/lib/*/usr/local/hadoop/mapreduce/*/usr/local/hadoop/contrib/capacity scheduler/*.jar一般来说,在bash中使用for循环的方式是不受欢迎的(尽管您明确希望字符串拆分和全局扩展,您的用例可能是个例外);你有没有注意到整个| sort | uniq
的东西都是无用的,因为sort
对行进行排序,但是tr
输出仍然是一个输出?如果要对hadoop类路径
输出进行排序,管道必须是hadoop类路径| tr':''\n'| sort | uniq
${(s/:/ou)$(hadoop classpath)}
for f in ${(s/:/ou)$(hadoop classpath)}; do echo $~f; done
for f in ${(s/:/ou)~$(hadoop classpath)}; do echo $f; done
print -l ${(s/:/ou)~$(hadoop classpath)}