FreeBSD를 사용하고 있는데 문자열의 일부로 {}가 포함된 명령을 실행하는 데 문제가 있습니다. 예를 들어, 찾은 파일의 이름을 바꾸는 경우입니다. 이것은 상황의 예입니다. 질문 자체는 "{}" 구문의 일반적인 문제를 해결하는 것입니다.
find . -type f -name 'data*' -execdir mv {} OLD_{} \;
find . -type f -name 'data*' -execdir mv {} archive/{} \;
-execdir
포함 디렉터리 내에서 명령을 실행하고 {}
파일 이름만 확장하면 포함 디렉터리 경로 관련 문제 를 피할 수 있습니다.
두 가지 질문이 있습니다:
-execdir mv
절에서 매개변수를 적절하게 인용하는 방법 (많은 파일의 이름에는 공백이나 작은따옴표가 있습니다).- 파일 이름을 완전히 바꾸도록 대상을 얻으려면 어떻게 해야 합니까?
두 번째 문제는 {}
"발견된 항목의 경로"가 선행/후행 공백으로 둘러싸인 경우에만 확장되는 것처럼 보이므로 명령 매개변수가 엉망이 되기 때문에 발생합니다. 공백이 아닌 선행 및 후행 문자의 예:
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):" {} \;
(result): .
(result): dir 1
(result): dir 2
(result): dir 3
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{} \;
(result):
(result):
(result):
(result):
# /usr/bin/find . -maxdepth 1 -execdir echo {}":(result)" \;
:(result)
:(result)
:(result)
:(result)
man find
지적"-exec 및 -ok rudimentary의 역사적 구현은 유틸리티 이름이나 유틸리티 인수의 앞이나 뒤에 공백이 아닌 문자가 있는 경우 문자열 "{}"를 대체하지 않았습니다. 유틸리티 이름의 어느 위치에 있든 이 버전은 다음 중 하나를 수행합니다. 교체하지 않으면 인수가 나타납니다.", 하지만 그런 일은 일어나지 않는 것 같습니다.
실행하려는 명령을 어떻게 실행합니까?
답변1
이 동작은 쉘로 사용될 때 find
FreeBSD 11.1-RELEASE에서 재현될 수 없습니다 . /bin/sh
나예전에는/bin/csh
하지만 두 가지 모두에서 재현할 수 있었습니다 /bin/tcsh
.
csh
및 에서 이 문제를 해결하려면 을 (를) tcsh
인용 하거나 다음 방법을 사용하십시오.{}
\{\}
'{}'
문자열의 일부로 사용될 때 find
올바르게 확장되지 않는 구현에서 현재 경로 이름을 다른 문자열과 올바르게 연결 하려면 하위 쉘을 사용하여 수행할 수 있습니다.{}
예:
find . -type f -name '*.c' -exec sh -c 'printf "(result):%s\n" "$@"' sh {} +
또는 echo
(그러나 " 참조)왜 printf가 echo보다 나은가요?"),
find . -type f -name '*.c' -exec sh -c '
for name do
echo "(result):$name"
done' sh {} +
또는
find . -type f -name '*.c' -execdir sh -c '
for name do
mv -- "$name" "OLD_${name##*/}"
done' sh {} +
즉, sh -c
명령줄 인수로 하위 쉘(여기)에 대한 경로 이름을 제공한 다음 일반적으로 쉘 변수를 사용하는 것처럼 결과 쉘에서 해당 경로 이름을 사용하여 연결합니다.
( ${name##*/}
위의 내용은 GNU가 경로 이름을 사용할 때 경로 이름 앞에 추가하는 것을 방지하기 위한 것입니다 find
.)./
-execdir
관련된:
답변2
마지막 두 가지 예가 혼란 스럽습니다. 여기서 find
replacement 없이 {}
그대로 유지될 것이라고 생각하면 출력은 다음과 같습니다 (result):{}
.
# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{} \;
(result):
어쩌면 쉘이 이러한 foo{}
문자열을 손상시키고 있는 것일까요?
이것이 내가 tcsh
하는 일입니다.
$ tcsh -c 'echo foo{}bar '
foobar
$ tcsh -c 'echo foo {} bar '
foo {} bar
이것은또한 언급인용된 다른 질문에 대한 의견에서 {}
,GNU는 특정 셸에 대해 {}를 찾아 차단합니다. 어느 셸인가요?.
해결 방법: 다음을 포함하는 문자열 주위에 따옴표를 넣으세요 {}
.
$ tcsh -c 'echo "foo{}bar" '
foo{}bar
(인용이 필요한 또 다른 쉘은 {}
이지만 fish
개별 쉘도 제거합니다 {}
.)
답변3
다음과 같은 것을 사용하는 것이 더 나을 것입니다.
find . -type f -name 'data*' -print0 | xargs -0 -I % mv '%' 'OLD_%'
find -print0 | xargs -0을 사용하면 각 파일 이름이 널 바이트로 구분되므로 파일 이름의 공백 및 기타 특수 문자로 인해 파일 이름이 두 개의 개별 인수로 분할되지 않습니다.