find および -exec を使用してファイルを検索し、親ファイルへのシンボリックリンクを作成します。

find および -exec を使用してファイルを検索し、親ファイルへのシンボリックリンクを作成します。

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-execln-vs./Users/me/OfflineFolder;
  • 一致するものが*.mdある場合、コマンドは、、、、、、、…です(サブディレクトリから呼び出されたすべてのファイルはありません)。bar.mdfoo.mdfind.-namefoo.md-execfindsomething-other-than-foo.md
  • *.md一致する場合、bar.mdコマンドfoo.mdfind、、、、、、、、… ​(構文エラーについて文句を言います)。.-namebar.mdfoo.md-execfind

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' _ {} +

おすすめ記事