파일 이름이 배열에 있는 경우에만 파일을 새 디렉터리에 복사합니다.

파일 이름이 배열에 있는 경우에만 파일을 새 디렉터리에 복사합니다.

나는 그다지 열정적이지는 않지만 zsh누군가에게는 쉬운 레이업이 될 것이라고 생각합니다.

sourcedirtargetdir다음 명령을 사용하여 파일을 일괄 복사 할 수 있습니다 .

$ find sourcedir -type f -exec cp {} targetdir \;

cp파일 이름이 다음 중 하나와 일치하는 경우에만 이 작업을 수행할 수 있습니다.

top directory
  fileA            <= directories have same name as single file they enclose
    fileA.pdf                    <= file to move
  another file
    another file.pdf             <= do NOT move
  still133another4544file
    still133another4544file.pdf  <= file to move

말 그대로 수천 가지를 거쳐야 하지만 선택한 몇 개만 이동하려면 이름이 있고 파일에 넣거나 스크립트에 직접 넣을 수 있습니다.
간단한 쉘 스크립트를 유지할 수 있습니다.' Ruby도 허용합니다.

답변1

먼저, 제가 만든 예제 트리는 다음과 같습니다.

{   pwd
    for   d in ./*/
    do    cd "$d"
          printf '\t%s\n' "${d#??}"
          printf '\t\t%s\n' *
          cd ..
    done
}

/tmp/top
        another file/
                another file.pdf
        fileA/
                fileA.pdf
        otherdir/
                1
                2
                3
        stillanother4544file/
                stillanother4544file.pdf

이제 아무것도 할 필요가 없습니다. 예제 트리를 미러링하기 위해 트리를 만들고 몇 가지 이상값을 추가했습니다.

하지만 나는 이렇게 하라고 제안합니다:

pax -rwis'|^./\([^/]*\)/\(\1\....\)$|\2|' -s'|.*||' . ../tgt

pax이는 기본적으로 -read/ rite 모드(직접 복사 모드) -w에서 POSIX 표준 유틸리티를 호출하는 것 입니다. 복사 모드에서는 의식 모드 처럼 아카이브가 생성되지 pax않지만 해당 인수에서 발견된 모든 파일(여기서는 현재 디렉터리에 뿌리를 둔 트리에만 해당) 및/또는 표준 입력((이 경우 비어 있음)에서 이름을 지정합니다. 마지막 인수로 명명된 디렉터리입니다(여기에 있음) .tar-w[...pattern...].../tgt

이제 나는 당신이 전체 트리를 옮기고 싶지 않다는 것을 압니다. 이 중 대부분은 -s대체 인수를 통해 처리됩니다. sed여기서 했던 것처럼 정규식 스타일 문을 기반으로 출력 파일의 이름을 바꿀 수 있습니다 . 나는 두 개의 -s교체 문을 사용합니다. 첫 번째는 모든 입력 일치 항목의 이름을 다음과 같이 바꿉니다.

./dirname/samename.???

...도착하다...

samename.???

...출력 시 복사된 모든 파일에 직접 맞도록 출력 트리를 평면화합니다 ../tgt. ./dirname/각 디렉토리의 상위 디렉토리를 유지하려면 \2첫 번째 -s교체된 부분을 &.

파일 이름이 pax -s성공적으로 일치하고 대체에 의해 수정되면 나머지 -s대체 매개변수는 다시 시도되지 않으며 아직 일치하지 않은 파일에만 나머지 이름 바꾸기 정규 표현식이 시도됩니다. 이는 현재 디렉토리의 모든 파일이 다음 유형의 경로 이름을 갖는다는 것을 의미합니다.

./dirname/samename.???

...마지막 경로 이름 구성 요소와 첫 번째 -s대체 문 및 모든 항목을 제외한 모든 항목을 성공적으로 선택하고 삭제했습니다.다른현재 디렉토리의 파일은 두 번째 대체 파일로 완전히 대체됩니다 -s|.*|| -s. 이는 pax빈 문자열을 대체하는 파일 이름이 처리 목록에서 제거되기 때문입니다 .

pax모든 작업이 처리 되면 선택한 파일을 실제로 복사하기 전에 마지막 작업 하나를 완료해야 합니다. 이것은 -i제가 지정한 대화형 이름 바꾸기 옵션이며 이것이 바로 여러분에게 적합할 수 있다고 생각합니다.

에서 man pax:

-i

  • 대화형으로 파일 이름이나 아카이브 멤버 이름을 바꿉니다. 일치하는 각 프로필 구성원에 대해pattern피연산자 또는 파일 피연산자와 일치하는 각 파일은 파일 이름, 파일 모드 및 수정 시간을 묻는 pax메시지를 표시합니다 . 그런 다음 에서 한 행을 읽습니다 . 이 줄이 비어 있으면 파일 또는 아카이브 멤버를 건너뜁니다. 행이 단일 마침표로 구성된 경우 파일 또는 아카이브 멤버는 이름을 수정하지 않고 처리됩니다. 그렇지 않으면 해당 이름이 해당 줄의 내용으로 대체됩니다. 다음과 같은 경우 0이 아닌 종료 상태로 즉시 종료됩니다./dev/ttypax/dev/ttypaxEOF응답을 읽는 동안 발생했거나 /dev/tty읽기 또는 쓰기를 위해 열 수 없습니다.

ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 stillanother4544file.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input > .
Processing continues, name unchanged.

ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 another file.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input >
Skipping file.

ATTENTION: pax interactive file rename operation.
-rw-r--r-- Oct 17 04:30 fileA.pdf
Input new name, or a "." to keep the old name, or a "return" to skip this file.
Input > .
Processing continues, name unchanged.

-i따라서 현재 디렉터리에 있는 6개 파일 중 이 세 개만 대화형 이름 바꾸기 프롬프트에 들어갈 수 있으며 , 이 세 개 중 두 개만 다음과 같습니다 ../tgt.

ls -l ../tgt

-rw-r--r-- 1 mikeserv mikeserv 0 Oct 17 04:30 fileA.pdf
-rw-r--r-- 1 mikeserv mikeserv 0 Oct 17 04:30 stillanother4544file.pdf

답변2

이러한 파일 이름을 배열에 저장하고 그 중 어느 것도 포함되지 않은 경우 |(이스케이프가 필요하지만 귀찮게 할 가치가 없으므로 아래 대안 참조) 간단히 배열의 요소를 연결하고 결과를 다음과 같이 사용할 수 있습니다. 덩어리 :

myarr=( file1 file2 ... fileN )
lst=${(j:|:)myarr}
cp -v -- **/($~lst) targetdir

예를 들어 다음 파일은 임의의 위치에 있습니다 /tmp.

caxZN.gif
e8ApF.gif
div2k.js.lzo
cmp2jz.ini

이름을 배열에 저장하면 결과는 다음과 같습니다.

cp -v -- **/($~lst) targetdir
'tmp/div2k.js.lzo' -> 'targetdir/div2k.js.lzo'
'tmp/다른 디렉토리/e8ApF.gif' -> 'targetdir/e8ApF.gif'
'tmp/다른 디렉토리/一two/cmp2jz.ini' -> 'targetdir/cmp2jz.ini'
'tmp/some dir/caxZN.gif' -> 'targetdir/caxZN.gif'

또는 이번에는 한 줄에 하나의 파일 이름이 있는 파일에 나열되어 있다고 가정하고 배열의 줄을 읽은 다음 다음을 사용할 수 있습니다.글로벌 예선/이자형그리고수정자전역/선택 배열의 ​​파일 이름만:

mylist=(${(f)"$(<list_of_files)"})
cp -- **/*(.e_'(($mylist[(Ie)$REPLY:t]))'_) targetdir

실제로는 .일반 파일만 선택하고( D목록에 점 파일이 포함된 경우 추가됨) 참인 파일 이름 e_'expression'_만 반환합니다 .expression기본 이름( $REPLY:t)배열의 요소입니다 mylist,예를 들어:

print -rl -- **/*(.e_'(($mylist[(Ie)$REPLY:t]))'_)
tmp/div2k.js.lzo
tmp/기타 디렉토리/e8ApF.gif
tmp/기타 디렉터리/one two/cmp2jz.ini
tmp/a 디렉토리/caxZN.gif

물론 위의 모든 내용은 zsh. 어쨌든, 파일 이름을 작성하고 올바른 옵션을 예를 들어
전달하는 데 시간을 투자하는 한 자신의 솔루션도 작동합니다.find

find . -type f \( -name caxZN.gif -o -name e8ApF.gif -o -name div2k.js.lzo -o -name cmp2jz.ini \) -exec cp -- {} targetdir \;

답변3

$ find sourcedir -type f -name 'file?.pdf' -exec cp {} targetdir \;

이 패턴과 일치하는 파일만 일치해야 합니다.

답변4

내가 올바르게 이해했다면 디렉터리당 하나의 파일만 있고 해당 파일이 목록에 있으면 이동하고 싶을 것입니다. 그렇다면 다음과 같이 할 수 있습니다.

while read -r file; do cp sourcedir/*/"$file" targetdir; done < list.txt

디렉토리도 복사해야 하는 경우 파일을 읽을 수 있습니다.파일 확장자 제거디렉터리 이름을 가져와서 다음을 복사하세요.

while read -r file; do cp -r sourcedir/"${file%.*}" targetdir; done < list.txt

관련 정보