나는 그다지 열정적이지는 않지만 zsh
누군가에게는 쉬운 레이업이 될 것이라고 생각합니다.
sourcedir
targetdir
다음 명령을 사용하여 파일을 일괄 복사 할 수 있습니다 .
$ 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
이는 기본적으로 -r
ead/ 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/tty
pax
/dev/tty
pax
EOF
응답을 읽는 동안 발생했거나/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