Joh*_*Siu 23 bash shell-script quoting wildcards
操作系统:OS X
bash:GNU bash,版本 3.2.57(1)-release (x86_64-apple-darwin16)
我希望 Time Machine 从我的所有 git/nodejs 项目中排除一组目录和文件。我的项目目录在~/code/private/
,~/code/public/
所以我试图使用 bash 循环来执行tmutil
.
如果我有一个计算的字符串变量k
,我如何使它在 for 循环之前或之前 glob :
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
Run Code Online (Sandbox Code Playgroud)
在下面的长版本中,您将看到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
Run Code Online (Sandbox Code Playgroud)
输出
它们没有被覆盖。不是我想要的。
~/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
Run Code Online (Sandbox Code Playgroud)
ilk*_*chu 24
您可以使用 强制进行另一轮评估eval
,但这实际上并不是必需的。(并且eval
在您的文件名包含特殊字符(如$
)时开始出现严重问题。)问题不在于通配符,而在于波浪号扩展。
如果变量未加引号,则在变量扩展后会发生 Globbing ,如下所示(*):
$ x="/tm*" ; echo $x
/tmp
Run Code Online (Sandbox Code Playgroud)
因此,同样,这与您所做的相似,并且有效:
$ 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
Run Code Online (Sandbox Code Playgroud)
但是使用波浪号它不会:
$ i="~/public/*"; j="*.launch"; k="$i/$j"
$ echo $k
~/public/*/*.launch
Run Code Online (Sandbox Code Playgroud)
Bash清楚地记录了这一点:
展开的顺序是:大括号展开;波浪号扩展,参数和变量扩展,...
波浪号扩展发生在变量扩展之前,因此变量内部的波浪号不会被扩展。简单的解决方法是使用$HOME
或 完整路径。
(* 从变量扩展 globs 通常不是你想要的)
另一件事:
当您遍历模式时,如下所示:
exclude="foo *bar"
for j in $exclude ; do
...
Run Code Online (Sandbox Code Playgroud)
请注意,正如$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
Run Code Online (Sandbox Code Playgroud)
要解决此问题,请使用数组变量而不是拆分的字符串:
$ 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
Run Code Online (Sandbox Code Playgroud)
作为一个额外的好处,数组条目还可以包含空格,而不会出现拆分问题。
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
Run Code Online (Sandbox Code Playgroud)
出于与之前相同的原因,我们必须使用$HOME
而不是~
,并且$dirs
需要在find
命令行上$pattern
不加引号以便拆分,但应该加引号以防止外壳意外扩展它。
(我认为您可以使用-maxdepth
GNU find 来限制搜索的深度,如果您关心的话,但这是一个有点不同的问题。)
您可以将其保存为数组而不是字符串,以便稍后在许多情况下使用,并在定义它时进行通配。在您的情况下,例如:
k=(~/code/public/*/*.launch)
for i in "${k[@]}"; do
Run Code Online (Sandbox Code Playgroud)
或者在后面的例子中,你需要eval
一些字符串
dirs=(~/code/private/* ~/code/public/*)
for i in "${dirs[@]}"; do
for j in $exclude; do
eval "for k in $i/$j; do tmutil addexclusion \"\$k\"; done"
done
done
Run Code Online (Sandbox Code Playgroud)