如何使bash glob成为字符串变量?
系统信息
操作系统:OS X. bash:GNU bash,版本3.2.57(1)-release(x86_64-apple-darwin16) 背景 我希望时间机器从我的所有git / nodejs项目中排除一组目录和文件.我的项目目录在?/ code / private /和?/ code / public /所以我试图使用bash循环来执行tmutil. 问题 精简版 如果我有一个计算的字符串变量k,如何在for循环之前或之前使其成为全局变量: i='~/code/public/*' j='*.launch' k=$i/$j # $k='~/code/public/*/*.launch' for i in $k # I need $k to glob here do echo $i done 在下面的长版本中,您将看到k = $i / $j.所以我不能在for循环中对字符串进行硬编码. 长版 #!/bin/bash exclude=' *.launch .classpath .sass-cache Thumbs.db bower_components build connect.lock coverage dist e2e/*.js e2e/*.map libpeerconnection.log node_modules npm-debug.log testem.log tmp typings ' dirs=' ~/code/private/* ~/code/public/* ' for i in $dirs do for j in $exclude do k=$i/$j # It is correct up to this line for l in $k # I need it glob here do echo $l # Command I want to execute # tmutil addexclusion $l done done done 产量 它们不是全球化的.不是我想要的. ~/code/private/*/*.launch ~/code/private/*/.DS_Store ~/code/private/*/.classpath ~/code/private/*/.sass-cache ~/code/private/*/.settings ~/code/private/*/Thumbs.db ~/code/private/*/bower_components ~/code/private/*/build ~/code/private/*/connect.lock ~/code/private/*/coverage ~/code/private/*/dist ~/code/private/*/e2e/*.js ~/code/private/*/e2e/*.map ~/code/private/*/libpeerconnection.log ~/code/private/*/node_modules ~/code/private/*/npm-debug.log ~/code/private/*/testem.log ~/code/private/*/tmp ~/code/private/*/typings ~/code/public/*/*.launch ~/code/public/*/.DS_Store ~/code/public/*/.classpath ~/code/public/*/.sass-cache ~/code/public/*/.settings ~/code/public/*/Thumbs.db ~/code/public/*/bower_components ~/code/public/*/build ~/code/public/*/connect.lock ~/code/public/*/coverage ~/code/public/*/dist ~/code/public/*/e2e/*.js ~/code/public/*/e2e/*.map ~/code/public/*/libpeerconnection.log ~/code/public/*/node_modules ~/code/public/*/npm-debug.log ~/code/public/*/testem.log ~/code/public/*/tmp ~/code/public/*/typings 解决方法
您可以使用eval强制进行另一轮评估,但这实际上并不是必需的. (当你的文件名包含像$这样的特殊字符时,eval开始出现严重问题.)问题不在于globbing,而在于tilde扩展.
如果变量未加引号,变量扩展后会发生Globbing,就像这里(*): $x="/tm*" ; echo $x /tmp 所以,同样的,这与你所做的类似,并且有效: $mkdir -p ~/public/foo/ ; touch ~/public/foo/x.launch $i="$HOME/public/*"; j="*.launch"; k="$i/$j" $echo $k /home/foo/public/foo/x.launch 但是对于波浪号,它不会: $i="~/public/*"; j="*.launch"; k="$i/$j" $echo $k ~/public/*/*.launch 对于Bash来说这是clearly documented:
Tilde扩展在变量扩展之前发生,因此变量内部的变量不会扩展.简单的解决方法是 (*从变量扩展globs通常不是你想要的) 另一件事: 循环遍历模式时,如下所示: exclude="foo *bar" for j in $exclude ; do ... 请注意,因为$exclude是不加引号的,所以它们都是拆分的,并且在这一点上也是全局的.因此,如果当前目录包含与模式匹配的内容,则会扩展为: $i="$HOME/public/foo" $exclude="*.launch" $touch $i/real.launch $for j in $exclude ; do # split and glob,no match echo "$i"/$j ; done /home/foo/public/foo/real.launch $touch ./hello.launch $for j in $exclude ; do # split and glob,matches in current dir! echo "$i"/$j ; done /home/foo/public/foo/hello.launch # not the expected result 要解决此问题,请使用array variable而不是拆分字符串: $exclude=("*.launch") $exclude+=("something else") $for j in "${exclude[@]}" ; do echo "$i"/$j ; done /home/foo/public/foo/real.launch /home/foo/public/foo/something else 作为一个额外的好处,数组条目也可以包含空格而不会出现拆分问题. 如果你不介意目标文件应该是什么目录级别,可以使用find -path做类似的事情.例如.找到以/e2e/*.js结尾的任何路径: $dirs="$HOME/public $HOME/private" $pattern="*/e2e/*.js" $find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js 出于与以前相同的原因,我们必须使用$HOME而不是?,并且$dirs需要在find命令行上不加引号,因此它会被拆分,但是$pattern应该被引用,因此它不会被shell意外扩展. (我认为你可以在GNU上使用-maxdepth来限制搜索的深度,如果你关心,但这是一个不同的问题.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |