システム情報
OS: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
コメント
回答
eval
を使用して、別の評価ラウンドを強制できますが、実際には必要ありません。(そしてeval
が始まりますファイル名に$
のような特殊文字が含まれていると、深刻な問題が発生します。)問題はグロビングではなく、ティルデ拡張にあります。
グロビングが発生します変数の展開後、変数が引用符で囲まれていない場合は、次のようになります(*):
$ 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の明確に文書化されています:
展開の順序は次のとおりです。brace展開、tilde展開、パラメーターおよび変数展開、…
チルダ展開は変数展開の前に行われるため、変数内のチルダは展開されません。簡単な回避策は、代わりに$HOME
またはフルパスを使用することです。
(*変数からグロブを展開することは通常、必要なことではありません)
別のこと:
ここのようにパターンをループするとき:
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
これを回避するには、分割された文字列の代わりに配列変数を使用します。
$ 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
ivを使用する必要があります以前と同じ理由で、~
の代わりにid = “09073477e5”>
を使用し、$dirs
の引用符を外す必要があります。find
コマンドラインで分割されるため、$pattern
は引用符で囲んで、シェルによって誤って展開されないようにする必要があります。
(GNUfindで-maxdepth
を試して、気になる場合は検索の深さを制限できると思いますが、それは「少し別の問題です。)
コメント
-
find
の答えはあなたですか? forループが複雑になっているので、私も実際にそのルートを探索しています。しかし、’ -path ‘に問題があります。 - チルダに関する情報としてのクレジット’〜’は主な問題により直接的です。最終的な台本と説明は別の回答で投稿します。しかし、あなたへの完全な信用:D
- @JohnSiu、ええ、findを使用することが最初に頭に浮かんだものでした。正確なニーズによっては、それも使用できる場合があります。 (または、一部の用途ではさらに良いです。)
- @kevinarpe、配列は基本的にそれだけを目的としていると思います。そうです、
"${array[@]}"
(引用符付き! )が文書化されており(ここおよびここを参照)、それらをさらに分割します。 - @sixtyfive、まあ、
[abc]
はグロブパターンの標準部分です。 div id = “716cf7cb1f”> 、私は’ここでそれらすべてをカバーする必要があるとは思わない’ 。
回答
文字列ではなく配列として保存して、後で使用することができます。定義するときにグロブを発生させます。たとえば、次のようになります。
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
コメント
-
$exclude
にワイルドカードが含まれていることに注意してください。’ dは、 split + glob 演算子を使用する前にグロブを無効にし、$i/$j
および使用しないために復元する必要があります。eval
ただし、"$i"/$j
- あなたとilkkachuの両方が良い答えを出します。しかし、彼の答えが問題を特定しました。
を使用します
回答
@ilkkachuの回答により、グロブの主な問題が解決しました。彼の功績です。
V1
ただし、exclude
にはワイルドカード(*)の有無にかかわらずエントリが含まれているため、存在しない場合があります。全体として、$i/$j
のグロビング後に追加のチェックが必要です。ここで調査結果を共有します。
#!/bin/bash exclude=" *.launch .DS_Store .classpath .sass-cache .settings 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=" $HOME/code/private/* $HOME/code/public/* " # loop $dirs for i in $dirs; do for j in $exclude ; do for k in $i/$j; do echo -e "$k" if [ -f $k ] || [ -d $k ] ; then # Only execute command if dir/file exist echo -e "\t^^^ Above file/dir exist! ^^^" fi done done done
出力の説明
以下は、状況を説明するための部分的な出力です。
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/a.launch ^^^ Above file/dir exist! ^^^ /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/b.launch ^^^ Above file/dir exist! ^^^ /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.DS_Store ^^^ Above file/dir exist! ^^^
上記は自明です。
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.classpath /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.sass-cache /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.settings /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/Thumbs.db /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/bower_components /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/build /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/connect.lock /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/coverage /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/dist
上記は除外エントリ($j
)にはワイルドカードがなく、$i/$j
はプレーンな文字列の連結になります。ただし、ファイル/ディレクトリは存在しません。
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
上記は除外エントリ($j
)として表示されます。ワイルドカードですが、ファイルとディレクトリが一致しません。$i/$j
のグロブは、元の文字列を返すだけです。
V2
V2は一重引用符を使用し、 eval
およびshopt -s nullglob
を使用すると、クリーンな結果が得られます。ファイル/ディレクトリの最終チェックは必要ありません。
#!/bin/bash exclude=" *.launch .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=" $HOME/code/private/* $HOME/code/public/* " for i in $dirs; do for j in $exclude ; do shopt -s nullglob eval "k=$i/$j" for l in $k; do echo $l done shopt -u nullglob done done
コメント
- 1つの問題は
、
$exclude
のグロブは、その$exclude
拡張時に拡張される可能性があります(そして呼び出します)eval
で問題が発生しています)。 ‘for i in $dir
とfor l in $k
でグロビングを有効にしたいが、。 ‘は、後者の前に
set -f
を、もう一方の前にset +f
を必要とします。より一般的には、’は、split + glob演算子を使用する前に調整する必要があります。いずれの場合も、’echo $l
にsplit + globは必要ないため、$l
そこに引用する必要があります。 - @St é phaneChazelasはv1またはv2を参照していますか? v2の場合、
exclude
とdirs
はどちらも一重引用符で囲みます(), so no globbing till
eval`。 - リストコンテキストで引用符で囲まれていない変数拡張でグロブが発生します。これは(変数を引用符で囲まないままにする)ことです。 split + glob 演算子を呼び出すこともあります。’スカラー変数への割り当てにグロブはありません。
foo=*
およびfoo='*'
は同じですが、echo $foo
とecho "$foo"
は同じではありません(、’は、zsh、fish、rcなどのシェルで修正されました。上記のリンクも参照してください)。実行その演算子を使用したいが、ある場所では分割部分のみ、他の場所ではグロブ部分のみを使用する。
- @St é phaneChazelasありがとうございます情報!!!いつか私を連れて行ったが、私は今懸念を理解している。これは非常に貴重である!!ありがとう!!!
回答
zsh
の場合:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
は$array[1]string $array[2]string...
。 $=var
は変数に対して単語分割を実行し(他のシェルはデフォルトで実行します!)、$~var
は変数に対してグロブを実行します(他の何か)シェルもデフォルトで(通常、望まない場合は、他のシェルで上記の$f
を引用する必要があります))。
(N)
は、その$^array1/$^array2
nullglob をオンにするグロブ修飾子です。
拡張。これにより、グロブが一致しない場合はゼロに拡張されます。また、~/code/private/foo/Thumbs.db
のような非グロブが1つになります。つまり、その特定のものが存在しない場合は、含まれていません。
コメント
- これは本当に素晴らしいです。テストして動作しました。ただし、zshは改行に対してより敏感であるようです。一重引用符を使用します。
exclude
の囲み方が出力に影響します。 - @JohnSiu、そうです、あなたは’正解です。空の要素を確実に破棄するには、split + globと
$^array
を2つの別々の手順で実行する必要があるようです(編集を参照)。これは少し似ています。zsh
のバグ、’メーリングリストで問題を提起します。 - v2を思いつきます。 bashの場合、よりクリーンですが、zshスクリプトほどコンパクトではありません。lol
回答
古い投稿しましたが、ここでつまずいたので、これが他の人に役立つかもしれないと思った:)
bashにはkinda-glob()関数があります…これは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
k
は計算された文字列であり、staが必要ですyループまでそのように。長いバージョンを確認してください。