대상 파일 이름을 조정할 필요가 없으면 다음을 수행할 수 있습니다.
$ find -type f -name '*.pat' -print0 | xargs -O cp -t /path/to/dest
파일 이름에 개행 문자가 포함될 수도 있으므로 안전합니다.
대안:
$ find -type f -name '*.pat' -print0 | cpio -p -0 -d /path/to/dest
이제 내가 가진 문제는 대상이 VFAT 파일 시스템이기 때문에 파일 이름에 특정 문자(예: "?")가 허용되지 않는다는 것입니다. 이는 대상 파일 이름을 조정해야 함을 의미합니다.
그것은 마치
for i `find -type f -name '*.pat'` ; do
cp "$i" `echo $i | sed 's/?/_/'`
done
공백이 없는 파일 이름에만 작동합니다. IFS를 줄 바꿈으로 변경할 수 있습니다. 하지만 '\0'을 IFS로 설정하려면 어떻게 해야 합니까?
그리고 - for 루프는 파일(mv/sed)만큼 많은 포크/exec를 발생시킵니다. 이는 처음의 두 예제에서 요구하는 몇 개의 포크/exec보다 훨씬 더 많습니다.
이 문제를 해결하기 위한 대안은 무엇입니까?
답변1
pax
최소한 Debian, Suse, OpenBSD, NetBSD에서 발견됩니다.
find . -type f -name '*.pat' -print0 | pax -0rws'/?/_/gp' /path/to/dest/
pax
는표준 유틸리티(동일 tar
또는 반대 cpio
), -0
일부 구현에서 찾을 수 있지만 해당 옵션은 그렇지 않습니다.
?.pat
파일 과 파일이 모두 _.pat
존재하는 경우 동일한 이름으로 대체되므로 대상에서 하나가 다른 파일을 덮어쓰게 됩니다. 마찬가지로 _
및 디렉터리 가 있는 경우 해당 내용이 대상 디렉터리 ?
에 병합됩니다 ._
GNU sort
및 GNU를 사용하면 uniq
충돌을 미리 확인할 수 있습니다.
find . -type f -name '*.pat' -print0 |
tr '?' _ |
sort -z |
uniq -zd |
tr '\0' '\n'
충돌하는 파일을 보고합니다(디렉토리는 제외).
zsh
'를 사용하여 zmv
충돌을 해결할 수 있지만 이는 여전히 mkdir
파일당 1 더하기 1을 의미합니다.cp
autoload zmv
mkdir-and-cp() {mkdir -p -- $3:h && cp $@}
zmv -n -Qp mkdir-and-cp '(**/)*.pat(D.)' '/path/to/dest/$f:gs/?/_/'
(행복하면 삭제 -n
).
답변2
이는 GNU tar를 사용하여 달성할 수 있습니다.
$ find -type f -name '*.pat' -print0 | tar -c -f - --null --files-from - \
| tar -C /path/to/dest -v -x -f - --show-transformed --transform 's/?/_/g'
이점:
- 3개의 포크/exec만 필요합니다(파일 수에 관계 없음).
bash
소스 파일 선택은 매우 유연합니다. (GNU) find의 모든 기능을 사용할 수 있습니다(예: 또는ksh93
allowed 와 같은 간단한 쉘 와일드카드보다 더 구체적이어야 하는 경우**
).- 개행 문자가 포함된 파일 이름에도 안전합니다.
s/[^A-Za-z0-9 _-]/_/g
변환 부분도 매우 유연합니다. 예를 들어 문자 클래스에 없는 모든 문자를 다음과 같은 것으로 바꿀 수 있습니다.[A-Za-z0-9 _-]
답변3
이 문제를 해결하는 가장 좋은 방법은 다음을 정의하는 것입니다.허용하다문자를 정의하려고 하기보다는 문자를 정의하고 전치를 우회합니다.도착하다바꾸어 놓다. 허용되는 제한된 문자 집합을 정의하는 것보다 더 복잡합니다. 허용되는 문자 집합에 대한 특정 규칙이 있기 때문입니다.어디파일 이름에. 따라서 이것은 안전해야 하는 제한된 하위 집합을 사용합니다(FAT32 문서를 읽어서 아는 한). 다음과 같이 작동해야 합니다(bash4+).
#!/bin/bash
shopt -s globstar nullglob
for file in **/*.pat; do
echo cp -t "${1:-.}" "${file//[![:alnum:].\-_\/]/_}"
done
그러면 다음과 같은 내용이 반환됩니다.
$ > bar\?.pat
$ > baz\*.pat
$ > foo.pat
$ ./script foo
cp -t baz*.pat foo/baz_.pat
cp -t bar?.pat foo/bar_.pat
cp -t foo.pat foo/foo.pat
복사할 디렉터리를 첫 번째 인수로 전달합니다. echo
출력되는 작업을 계속 수행하려면 에 대한 호출을 제거하세요.