我正在尝试使用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
你有两个问题,都与事情发生的顺序有关。
find . -name *.md -exec ln -vs $(dirname {}) "$OUTPUT_DIR" \;
是一个单一的命令。 shell 在执行之前对其进行解析。解析步骤中:
$(…)
是命令替换,因此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
.为此,您需要一个工具,例如 shell,它是命令替换。
因此,策略如下:告诉 find 调用 shell,并告诉该 shell 运行涉及ln
和 的命令dirname
。您需要注意引用。将 shell 命令放在单引号中以避免其特殊字符被外壳解释。还要将模式放在-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' _ {} +