문자열에서 cp를 통해 여러 파일 복사

문자열에서 cp를 통해 여러 파일 복사

쉘스크립트 내에서 여러 파일을 디렉토리로 복사하려고 합니다. 이러한 파일에는 공백, 대괄호 등 모든 종류의 "불쾌한" 문자가 포함되어 있습니다. 그러나 나는 이것을 탈출하는 데 갇혀 있고 이것을 매우 이상하게 처리하는 것 같습니다 bash.cp

시나리오는 다음과 같습니다.
내 셸 내에서 이 명령을 실행하면 매력처럼 작동합니다.
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/

그러나 문자열에서 복사할 파일을 읽을 때 갑자기 이상해집니다 .
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/

cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory

cp나는 이것이 변수를 문자열로 제공하고 여러 파일이지만 하나의 파일이라고 믿기 때문이라고 생각합니다 . 변수를 따옴표( )로 묶지 않고 동일한 명령을 실행하면 var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/훨씬 더 이상한 오류가 발생합니다.
cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory

내 탈출을 완전히 무시하는 것 같습니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

편집하다:// 그런거같아파일 목록 복사에 대한 답변이 있지만 xargs여기서 bash가 왜 그렇게 이상하게 행동하는지 여전히 궁금합니다.

답변1

선생님, 이렇게 하면 아파요…

그럼 그러지 마세요!

진지하게, 단일 변수에 공백이 포함된 공백으로 구분된 파일 이름 목록을 할당하려는 이유는 무엇입니까? 그것은 재앙의 비결입니다.

귀하의 암시적인 질문에 대답하려면 귀하가 표시하는 오류 메시지가 귀하가 사용했다고 말하는 명령에 적합하지 않다는 데 동의합니다.

복사할 파일을 결정하는 방법을 언급하지 않았으므로 어떤 솔루션이 효과가 있고 어떤 솔루션이 효과가 없는지 알기가 어렵습니다. 가능한 접근 방식은 다음과 같습니다.


f1="/somedir/a file.png"
f2="/somedir/다른 파일.png"
cp "$f1" "$f2" "$f3" … /someotherdir

/( 끝에는 필요하지 않지만 /someotherdir/나쁠 것도 없습니다.) 이는 복사해야 할 최대 파일 수를 미리 알아야 한다는 사실로 인해 제한되며 일부가 필요합니다. f각 변수에 할당할 변수를 알아내는 방법입니다 .

이것은 지금 하고 있는 일과 비슷합니다.

var="/somedir/a\ file.png /somedir/another\ file.png …"
에코 "$var" | 읽는 동안 f1 f2 f3 …
하다
    cp "$f1" "$f2" "$f3" … /someotherdir
완료

read명령은 $var일반 공백에서 문자열을 분리하고 백슬래시를 제거하여 (공백) 만 남깁니다.  하지만 복사해야 할 최대 파일 수를 미리 알아야 합니다. ( while이 루프가 한 번만 반복되더라도 루프 구성이 필요합니다 .)

아마도 이것은 가까워지고 있습니다.

파일 목록=/tmp/my_filelist.$$
echo "/somedir/a file.png" >> "$filelist"
echo "/somedir/다른 파일.png" >> "$filelist"
파일 이름을 읽는 동안
하다
    cp "$filename" /someotherdir
완료 < "$filelist"

또 다른 변형은 가변 배열을 사용하는 것입니다. 매뉴얼 페이지 에서 그 방법을 배울 수 있습니다 bash.

답변2

Bash는 명령줄을 구문 분석할 때 먼저 따옴표, 이스케이프 등을 해석한 다음 변수를 바꿉니다. 변수 값에 이스케이프, 따옴표 등이 있는 경우 명령에 포함될 때쯤에는 의도한 효과를 내기에는 너무 늦기 때문에 효과가 전혀 적용되지 않습니다.

일반적으로 이러한 종류의 문제를 처리하는 가장 좋은 방법은 파일 이름을 문자열 변수가 아닌 배열(파일 이름당 하나의 요소)에 저장하는 것입니다. 그런 다음 "${array[@]}"각 요소가 별도의 "단어"로 처리되는 배열을 확장하는 데 사용합니다 .

files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/

업데이트: 파일 목록을 동적으로 작성해야 하는 경우 배열을 사용하면 쉽습니다.

files=() # Start with an empty list
for newfile in somethingorother; do
    files+=("$newfile")
done

답변3

Bash를 사용하는 경우 이스케이프 문자를 사용하지 않으려면 파일 이름을 큰따옴표로 묶을 수 있습니다.

cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp

#!/bin/bash쉘스크립트에서 사용할 때 대신 이것을 사용하십시오 :

cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"

귀하의 경우 변수 내부의 값을 사용하고 있지만 변수를 잘못 전달했습니다. 변수에 공백이 있으면 큰따옴표로 묶어야 합니다.

var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/

큰따옴표를 사용하지 않으면 여러 변수를 공백으로 구분하여 사용하는 것과 같습니다.

관련 정보