使用 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
)。謝謝
你有兩個問題,都與事情發生的順序有關。
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.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
. 為此,您需要一個工具,例如 shell,它是一個命令替換。所以這裡的策略是:告訴 find 呼叫一個 shell,並告訴那個 shell 執行涉及
ln
and的命令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' _ {} +