システムメッセージ
オペレーティングシステム:OS X
bash: GNU bash, バージョン 3.2.57(1)-リリース(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
答え1
強制を使用して別の評価を実行することもできますが、eval
実際にはそうする必要はありません。 (eval
ファイル名になどの特殊文字が含まれると深刻な問題が発生し始めます$
。)問題はワイルドカードではなくチルダ拡張です。
ワイルドカード発生後ろに変数拡張、変数が引用されていない場合は次のようになります(*)。
$ x="/tm*" ; echo $x
/tmp
引用符のない拡張で発生するもう1つのことはトークン化です。IFS
これは、問題のパターンに文字(通常はスペース)が含まれている場合に問題になります。この問題を回避するには、IFS
単語の区切りを空の文字列に設定して無効にする必要があります。
もう一度申し上げますが、これはあなたがしたことに似ており、うまくいきます。
$ IFS=
$ 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
これは明確に文書化大きな打撃の場合:
拡張順序は、中かっこ拡張、パラメータおよび変数拡張、...です。
チルダ拡張は変数拡張の前に行われるため、変数内のチルダは拡張されません。簡単な修正方法は、代わりにまたは$HOME
フルパスを使用することです。
(*変数からglobを拡張することは通常望むものではありません)
もう一つのこと:
次のようにパターンを繰り返します。
exclude="foo *bar"
for j in $exclude ; do
...
$exclude
引用されていないので分割され、ワイルドカードとして表示されます。したがって、現在のディレクトリにこのパターンに一致するエントリが含まれている場合は、次のように展開されます。
$ IFS=
$ i="$HOME/public/foo"
$ exclude="*.launch"
$ touch $i/real.launch
$ for j in $exclude ; do # glob, no match
printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/real.launch
$ touch ./hello.launch
$ for j in $exclude ; do # glob, matches in current dir!
printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/hello.launch # not the expected result
この問題を解決するには、次を使用します。配列変数文字列を分割する代わりに:
$ IFS=
$ exclude=("*.launch")
$ exclude+=("*.not this")
$ for j in "${exclude[@]}" ; do printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/real.launch
/home/foo/public/foo/some file.not this
ただし、パターンが何も一致しない場合、デフォルトではそのまま残ります。したがって、ディレクトリが空の場合は.../*.launch
印刷されます。
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
-maxdepth
(興味があればGNU findを使用して検索の深さを制限できると思いますが、それは別の質問です。)
答え2
後で複数の状況で使用するために文字列の代わりに配列として保存することができ、定義時にグロービングが発生するようにすることができます。あなたの場合、例えば次のようになります。
k=(~/code/public/*/*.launch)
for i in "${k[@]}"; do
または、後の例では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
答え3
そしてzsh
:
exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
...
'
dirs=(
~/code/private/*
~/code/public/*
)
for f ($^dirs/${^${=~exclude}}(N)) {
echo $f
}
${^array}string
isに拡張されます$array[1]string $array[2]string...
。$=var
isは変数(他のシェルではデフォルトでこれを行います!)と$~var
globbing変数(他のシェルではデフォルトでこれを行います(通常はこれをしたくない場合は、上記$f
の他のシェルを参照)を実行します。する必要があります。))。
(N)
グローバル修飾子がオンになっていますか?空のボールこの拡張パックで製造された各球について$^array1/$^array2
。これが一致しない場合、globは空になります。これは、non-globを1に置き換えた場合にも発生します~/code/private/foo/Thumbs.db
。つまり、特定の項目が存在しない場合は含まれません。
答え4
古い投稿ですが、ここで偶然発見して他の人に役立つと思いました:)
bashにはglob()に似た関数があります。 1行に1つの値を出力するcompgen -Gと呼ばれるので、動作するにはreadarrayが必要です。
この試み:
i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'
readarray -t files < <(compgen -G "$k") # $k is globbed here
for i in "${files[@]}"
do
echo "Found -$i-"
done