システムメッセージ

システムメッセージ

システムメッセージ

オペレーティングシステム: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代わりに使用し、コマンドラインから引用符をオフにして分割する必要がありますが、誤ってシェルから拡張されないように引用する必要があります。~$dirsfind$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}stringisに拡張されます$array[1]string $array[2]string...$=varisは変数(他のシェルではデフォルトでこれを行います!)と$~varglobbing変数(他のシェルではデフォルトでこれを行います(通常はこれをしたくない場合は、上記$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

関連情報