
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" \;
단일 명령입니다. 쉘은 이를 실행하기 전에 이를 구문 분석합니다. 구문 분석 단계에서:
$(…)
은 명령 대체이므로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' _ {} +