Shell-Script

使用 find 和 -exec 查找文件並與父文件建立符號連結

  • June 16, 2017

我正在嘗試使用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)。謝謝

你有兩個問題,都與事情發生的順序有關。

find . -name *.md -exec ln -vs $(dirname {}) "$OUTPUT_DIR" \;是一個單一的命令。Shell 在執行之前對其進行解析。在解析步驟中:

  • $(…)是一個命令替換,所以dirname {}被執行,產生.(沒有目錄部分{})。
  • $OUTPUT_DIR是一個變數替換,所以它被它的值替換。
  • *.md是一個 glob 模式,因此它被匹配文件列表替換。如果沒有匹配項,則模式保持不變。

這樣就完成了確定要執行什麼命令所需的工作。

  • 如果目前目錄中沒有匹配*.md的文件,則使用給定參數執行以下命令:find, ., -name, *.md, -exec, ln, -vs, ., /Users/me/OfflineFolder, ;.
  • 如果*.md匹配bar.mdfoo.md則命令為find, ., -name, foo.md, -exec, … (並且find將錯過something-other-than-foo.md子目錄中呼叫的任何文件)。
  • 如果*.md匹配bar.mdfoo.md那麼命令是find, ., -name, bar.md, foo.md, -exec, … (並且find會抱怨語法錯誤)。

您想對dirname查找結果執行。這意味著您需要指示find執行dirname. 您可以這樣做,但 find 沒有任何機制來收集輸出dirname並將其作為參數傳遞給ln. 為此,您需要一個工具,例如 shell,它是一個命令替換。

所以這裡的策略是:告訴 find 呼叫一個 shell,並告訴那個 shell 執行涉及lnand的命令dirname。您需要注意引用。將 shell 命令放在單引號中以避免其特殊字元被外殼解釋。還要將模式 for-name放在引號中,以便它傳遞給find外殼而不是由外殼擴展。

find . -name '*.md' -exec sh -c 'ln -vs …' \;

下一步是完成. 不要{}在 shell 命令中使用:這只會將文件名作為 shell 程式碼片段放置,並且任何特殊字元都將由內 shell 解析。相反,將 給出的文件名find作為參數傳遞給 shell 腳本。後面的第一個參數是 shell 實例的名稱 ( ),但您可以將其用於任何您喜歡的目的;隨後的參數是位置參數(,,,… )。sh -c *CODE*``$0``$1``$2

find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$1"' {} "$OUTPUT_DIR" \;

我已經$OUTPUT_DIR作為參數傳遞給腳本。在這裡沒關係,因為該值不包含任何 shell 特殊字元,但這是一個好習慣,你永遠不知道何時有人可能會更改路徑以包含空格。另一種可能性是通過環境傳遞它:

export OUTPUT_DIR
find . -name '*.md' -exec sh -c 'ln -vs "$(dirname "$0")" "$OUTPUT_DIR"' {} \;

代替dirname,您可以使用文本替換:刪除最後一個斜線之後的所有內容。您不必擔心沒有目錄部分的特殊情況,因為通過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' _ {} +

引用自:https://unix.stackexchange.com/questions/371434