find
特定のパターンに一致するファイル検索を使用し、親ディレクトリを別のディレクトリにシンボリックリンクしようとしています。これが現在のスクリプトです。 (私はMacでこれをやっているので、ここでは-printfは動作しません。)
#!/usr/bin/env bash
PROCESS_DIR="/Users/me/Google Drive/Me"
OUTPUT_DIR="/Users/me/OfflineFolder"
# Copy directory structure and then make symlinks
rm -R "$OUTPUT_DIR";
mkdir "$OUTPUT_DIR";
cd "$PROCESS_DIR";
find . -name *.md -exec ln -vs $(dirname {}) "$OUTPUT_DIR" \;
しかし、うまくいかないようです。ただし、このスクリプトは機能します(私のコンピュータ上のすべてのmdファイルへのシンボリックリンクを作成します。
# Copy directory structure and then make symlinks
rm -R "$OUTPUT_DIR";
mkdir "$OUTPUT_DIR";
cd "$PROCESS_DIR";
find . -type d -exec mkdir -vp "$OUTPUT_DIR/{}" \;
find . -name *.md -exec ln -vs "$(pwd)/{}" "$OUTPUT_DIR/{}" \;
find "$OUTPUT_DIR" -type d -empty -delete
なぜこれがうまくいかないのか知っていますか?私はいくつかの異なるアプローチを試しました(find $PROCESS_DIRECTORY
代わりに使用を含むcd $PROCESS_DIRECTORY
)。ありがとう
ベストアンサー1
2つの質問があります。どちらも仕事が発生した順序に関連しています。
find . -name *.md -exec ln -vs $(dirname {}) "$OUTPUT_DIR" \;
単一のコマンドです。シェルはそれを実行する前にそれを解析します。解析段階で:
$(…)
はコマンドオーバーライドなので、実行され、dirname {}
結果が表示されます.
(のディレクトリ部分なし{}
)。$OUTPUT_DIR
変数置換なので、その値に置き換えられます。*.md
グローバルパターンなので、一致するファイルのリストに置き換えられます。一致するものがない場合、パターンは変更されません。
これにより、実行するコマンドを決定するために必要なタスクが完了しました。
- 現在のディレクトリに一致するファイルがない場合は、指定されたパラメータを使用して次の
*.md
コマンドが実行されます。find
、、、、、、、、、、、、。.
-name
*.md
-exec
ln
-vs
.
/Users/me/OfflineFolder
;
- 一致するものが
*.md
ある場合、コマンドは、、、、、、、…です(サブディレクトリから呼び出されたすべてのファイルはありません)。bar.md
foo.md
find
.
-name
foo.md
-exec
find
something-other-than-foo.md
*.md
一致する場合、bar.md
コマンドfoo.md
はfind
、、、、、、、、… (構文エラーについて文句を言います)。.
-name
bar.md
foo.md
-exec
find
dirname
検索結果を実行しようとしています。つまり、find
実行するには指示が必要ですdirname
。これは可能ですが、findには出力を収集してdirname
引数に渡すメカニズムはありませんln
。これを行うには、コマンド置換のシェルなどのツールが必要です。
したがって、戦略は次のようになります。 findにシェルを呼び出すように指示し、そのシェルにln
関連するコマンドを実行するように指示しますdirname
。引用に注意を払う必要があります。特殊文字がシェルから解釈されないように、シェルコマンドを一重引用符で囲みます。また、パターンがシェルによって拡張されずにシェルに渡される-name
ように、パターンを引用符で囲みます。find
find . -name '*.md' -exec sh -c 'ln -vs …' \;
次のステップは完了です…
。シェルコマンド内で使用しないでください{}
。これにより、ファイル名がシェルフラグメントとして扱われ、すべての特殊文字が内部シェルから解析されます。代わりに、指定されたファイル名がfind
シェルスクリプトに引数として渡されます。次の最初の引数はシェルインスタンスの名前()ですが、目的の目的で使用できます。後続の引数は位置引数(、、...)です。sh -c CODE
$0
$1
$2
find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$1"' {} "$OUTPUT_DIR" \;
$OUTPUT_DIR
スクリプトにパラメータとして渡しました。値にシェル特殊文字が含まれていないため、ここでは重要ではありませんが、たとえば、誰かが空白を含むようにパスを変更することができるかどうかわからないので、これは良い習慣です。もう一つの可能性は環境を通過することです。
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$OUTPUT_DIR"' {} \;
dirname
代わりに、テキスト置換を使用できます。最後のスラッシュの後ろのすべてを削除してください。ディレクトリ部分がない特別なケースについて心配する必要はありません。渡された filename ではそのようなことが発生しないからですfind
。
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'ln -vs "${0%/*}" "$OUTPUT_DIR"' {} \;
+
フォームを使用して-exec
作業を高速化できます。私は_
asを渡し、その後のパラメータは$0
ループが繰り返されるファイル名です。for
export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'for x; do ln -vs "${x%/*}" "$OUTPUT_DIR"; done' _ {} +