파일 목록을 복사하고 대상 파일 이름을 즉시 조정하는 방법은 무엇입니까?

파일 목록을 복사하고 대상 파일 이름을 즉시 조정하는 방법은 무엇입니까?

대상 파일 이름을 조정할 필요가 없으면 다음을 수행할 수 있습니다.

$ 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의 모든 기능을 사용할 수 있습니다(예: 또는 ksh93allowed 와 같은 간단한 쉘 와일드카드보다 더 구체적이어야 하는 경우 **).
  • 개행 문자가 포함된 파일 이름에도 안전합니다.
  • 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출력되는 작업을 계속 수행하려면 에 대한 호출을 제거하세요.

관련 정보