
두 개의 텍스트 파일이 있고 src.txt
여기 dest.txt
에 src.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
잘 모르겠습니다 . 계속해서 공백 문자 해결 오류가 발생합니다.grep
sed
cannot 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_file
dest.txt
grep
/filename$
F
dest.txt
src.txt
bash
4
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
두 번째 스크립트를 생성 합니다.sed
dest.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