파일 목록을 대상 인덱스와 일치시켜 이동

파일 목록을 대상 인덱스와 일치시켜 이동

두 개의 텍스트 파일이 있고 src.txt여기 dest.txtsrc.txt파일 이름 목록(일부 공백 포함)이 포함되어 있고 /src/dir/해당 dest.txt파일이 속한 전체 파일 경로(공백 포함) 목록이 무작위 순서로 포함되어 있다고 가정해 보겠습니다. 예를 들어:

src.txt:

file 1.jpg
file_2.html
file 3.jpg

대상.txt:

/dest/dir 1/file 3.jpg
/dest/file4.txt
/dest/file 5.txt
/dest/dir 2/file 1.jpg
/dest/file_2.html

셸에서 일괄 이동 작업을 어떻게 수행할 수 있나요? 나는 소스 파일에 대한 루프 작업을 해왔고 명령 while read을 사용해야 한다고 확신 하지만 여기에 필요한지 또는 필요한지 mv잘 모르겠습니다 . 계속해서 공백 문자 해결 오류가 발생합니다.grepsedcannot stat...

답변1

와 함께 zsh:

src=(${(f)"$(<src.txt)"})
for f (${(f)"$(<dest.txt)"})
(($src[(Ie)$f:t])) && mv /src/dir/$f:t $f

이는 배열의 각 파일을 읽은 다음 배열의 각 요소에 대해 읽습니다."목적지"배열, 기본 이름( 모든 선행 경로 이름 구성 요소를 제거하는 :t수정 zsh자)이"src"그런 다음 파일을 이동합니다. 테스트 실행을 수행하려면 mv로 교체하세요 printf '"%s" -> "%s"\n'.


이제 다음을 실행할 수도 있습니다(여전히 zsh).

for f (${(f)"$(grep -Ff src.txt dest.txt)"})
mv /src/dir/$f:t $f

의 파일 이름이 src.txt경로 목록의 디렉터리 이름(또는 해당 이름의 일부)과 일치 하지 않는 한 잘 작동합니다 dest.txt(예: 파일 이름 data1및 in src.txt과 같은 경로는 거짓 긍정을 제공합니다). 이를 방지하려면 에 있는 경로의 마지막 구성 요소만 일치하도록 고정된 문자열 대신 파일 이름을 패턴(예: 와 같은 정규식 사용 )으로 전달할 수 있습니다 . 하지만 이를 위해서는 의 파일 이름에 있는 모든 특수 문자(있는 경우)를 이스케이프해야 합니다 . 예를 들어 이번에는 ( )를 사용합니다./path/data1_dir/some_filedest.txtgrep/filename$Fdest.txtsrc.txtbash4

readarray -t files < <(sed 's|[[\.*^$/]|\\&|g;s|.*|/&$|' src.txt | grep -f- dest.txt)
for f in "${files[@]}"; do mv /src/dir/"${f##*/}" "$f"; done

답변2

줄 바꿈이 허용 가능한 구분 기호라면 POSIX 셸에서 다음이 매우 강력해야 합니다.

IFS='
';set -f
for   f in $(cat <"$destfile")
do    [ -e "./${f##*/}" ] ||
      [ -h "./${f##*/}" ] &&
      mv   "./${f##*/}"  "$f"
done

내가 상상할 수 있는 해당 솔루션에는 두 가지 가능한 문제가 있습니다.

  • 입력 파일 크기가 너무 커서 한 번에 분할할 수 없습니다.

    • 내 시스템에서는 입력이 수만 줄에 도달할 때까지 심각하게 고려하지도 않습니다.
  • 의 파일 이름이 $destfile현재 디렉토리에 존재할 수 있지만 여전히 있어야 합니다.~ 아니다어쨌든 감동받아라.

    • 이 솔루션은 두 개의 입력 파일을 완전히 비교하는 대신 각 마지막 경로 이름 구성 요소가 $destfile현재 디렉터리에 있는지 확인하기 때문에 파일 이름이 의도치 않게 일치하는 경우 이를 고려해서는 안 됩니다.

첫 번째 문제만 처리해야 하는 경우:

sed -ne"s|'|'"'\\&&|g' <"$destfile"    \
    -e "s|.*/\([^/].*\)|_mv './\1' '&'|p" | 
sh  -c '_mv(){ [ -e "$1" ]||[ -h "$1" ]&& mv "$@";};. /dev/fd/0'

귀하의 경우 sh끝에 삭제하고 다음을 사용할 dash수 있습니다 .. /dev/fd/0

sed ... | sh -cs '_mv(){ ...;}'

... dash이상하게도 명령줄과 표준 입력 호출 옵션을 모두 불평 없이 처리하기 때문입니다. 이식성이 좋지는 않지만 . /dev/fd/0이식성이 뛰어나기는 하지만 표준을 엄격하게 준수하지도 않습니다.

두 번째 문제가 우려되는 경우:

export  LC_ALL=C 
sed  -ne'\|/$|!s|.*/\(.*\)|\1/&|p' <"$destfile" |
sort -t/ -k1,1 - ./"$srcfile"  |  cut  -d/ -f2- |
sed  -e "\|/|!N;\|\n.*/|!d"    \
     -e "s|'|'"'\\&&|g'        \
     -e "s|\n|' '|;s|.*|mv './&'|" | sh

./"$srcfile"... 의 모든 파일 이름이 의 일부 경로 끝 부분에서 적절하고 동일하게 설명되는 한 매우 훌륭하게 처리되어야 합니다 "$destfile". sort두 개의 동일한 비교 중 더 짧은 항목이 항상 맨 위에 표시되므로 첫 번째 필드만 중요하고 파일 이름이 각 경로 이름의 머리 부분에 추가된 경우 두 파일의 "$destfile"병합 sort작업은 다음과 같은 시퀀스를 출력합니다.

$srcfile:  no /
$destfile: match
$destfile: unique
$destfile: unique
...
$srcfile:  no /
$destfile: match
$destfile: unique

...따라서 일치하지 않는 줄로 시작하는 줄 쌍에만 관심을 가지면 됩니다 /.

답변3

while read i; do echo cp \""$i"\" \"$(grep "/$i$" dst.txt)\"; done < src.txt

그러면 수행된 작업이 인쇄됩니다. echo실제로 파일을 복사하려면 제거하십시오 .

답변4

단일 라이너 스크립트는 스크립트를 생성하는 스크립트를 생성합니다.

sed이 예에서는 on 의 첫 번째 호출을 사용하여 파일을 복사하기 위한 쉘 스크립트를 생성하기 위해 실행될 src.txt두 번째 스크립트를 생성 합니다.seddest.txt

한 줄의 내용은 다음과 같습니다.

$ sed -n "$(sed 's,\(..*\),/\\/\1$/ { s/^/cp "\1" "/; s/$/";/; p; },' src.txt)" dest.txt #| sh -x

출력은 다음과 같습니다.

cp "file 3.jpg" "/dest/dir 1/file 3.jpg";
cp "file 1.jpg" "/dest/dir 2/file 1.jpg";
cp "file_2.html" "/dest/file_2.html";

#| sh명령 끝에 있는 주석을 참고하십시오 . 이렇게 하면 명령을 시도해보고 결과를 확인할 수 있으며, 괜찮다면 파이프의 주석 처리를 해제 sh하고 실제로 파일을 복사할 수 있습니다.

내부 sed 명령은 src.txt에서 sed 스크립트를 작성합니다. 생성된 스크립트의 첫 번째 줄은 다음과 같습니다.

/\/file 1.jpg$/ { s/^/cp file 1.jpg /; p; }

작동 방식은 다음과 같습니다.

입력:

    $ cat src.txt
    file 1.jpg
    file_2.html
    file 3.jpg

    $ cat dest.txt
    /dest/dir 1/file 3.jpg
    /dest/file4.txt
    /dest/file 5.txt
    /dest/dir 2/file 1.jpg
    /dest/file_2.html

첫 번째 sed호출. 이는 다음의 두 번째 호출로 해석될 생성된 스크립트를 보여줍니다 sed.

$ sed 's,\(..*\),/\\/\1$/ { s/^/cp "\1" "/; s/$/";/; p; },' src.txt
/\/file 1.jpg$/ { s/^/cp "file 1.jpg" "/; s/$/";/; p; }
/\/file_2.html$/ { s/^/cp "file_2.html" "/; s/$/";/; p; }
/\/file 3.jpg$/ { s/^/cp "file 3.jpg" "/; s/$/";/; p; }

셸 명령 대체를 사용하여 첫 번째 명령의 출력을 sed두 번째 호출에 전달된 명령줄의 스크립트로 사용합니다 sed.

$ sed -n "$(sed 's,\(..*\),/\\/\1$/ { s/^/cp "\1" "/; s/$/";/; p; },' src.txt)" dest.txt
cp "file 3.jpg" "/dest/dir 1/file 3.jpg";
cp "file 1.jpg" "/dest/dir 2/file 1.jpg";
cp "file_2.html" "/dest/file_2.html";

이제 xtrace 옵션( )을 사용하여 sed의 출력을 셸로 파이프합니다 sh -x. 파일이 하나도 없어서 오류가 발생합니다.

$ sed -n "$(sed 's,\(..*\),/\\/\1$/ { s/^/cp "\1" "/; s/$/";/; p; },' src.txt)" dest.txt  | sh -x
+ cp file 3.jpg /dest/dir 1/file 3.jpg
cp: cannot stat ‘file 3.jpg’: No such file or directory
+ cp file 1.jpg /dest/dir 2/file 1.jpg
cp: cannot stat ‘file 1.jpg’: No such file or directory
+ cp file_2.html /dest/file_2.html
cp: cannot stat ‘file_2.html’: No such file or directory

관련 정보