bash 쉘 스크립트에서 여러 와일드카드로 변수를 확장하려면 mv /rename을 사용하세요.

bash 쉘 스크립트에서 여러 와일드카드로 변수를 확장하려면 mv /rename을 사용하세요.

나는 데이터 파일에 지정된 매개변수를 기반으로 정적 폴더의 파일을 이동하는 Linux용 bash 쉘 스크립트를 작성했습니다.

내가 겪고 있는 문제를 보여주기 위해 스크립트를 재현 가능한 최소한의 예로 축소했습니다.

#!/bin/bash
while IFS=" " read -r fldr matchStr
do
    perlExp=\'s/^/\\/home\\/test\\/alpha\\/"$fldr"\\//\'
    fullmatchStr=testfile\ -\ "$matchStr"\ -\ *.txt
    rename --verbose "$perlExp" ${fullmatchStr}
done < test.dat

(저는 원래 mv대신을 사용하기 시작했으며 rename두 명령 중 하나를 사용할 수 있습니다.)

스크립트는 test.dat다음 구문으로 구성된 텍스트 파일을 읽습니다.

folderName matchStr
...

이 파일의 간단한 예 test.dat:

test001 *.foo.bar
test002 *.foo.baz
test003 bar.*.baz

이 데이터 파일에 와일드카드가 허용되고 스크립트가 와일드카드를 지원하는 것이 중요합니다. 또한 파일 이름의 공백을 지원해야 합니다.

이동할 파일의 파일 이름 예시:

testfile - linux.foo.bar - 10101010 10101010.txt
testfile - unix.foo.bar - 01010101 10101010.txt
testfile - ubuntu.foo.baz - 10101010 01010101.txt
testfile - debian.foo.baz - 10101010 00000000.txt
testfile - bar.linus.baz - 11111111 01010101.txt

test.dat파일이 주어지면 이러한 파일을 개별적으로 다음 폴더로 이동해야 합니다.

/home/test/alpha/test001
/home/test/alpha/test001
/home/test/alpha/test002
/home/test/alpha/test002
/home/test/alpha/test003

다음을 포함하여 여러 관련 QA를 읽었습니다.

하지만 여전히 원하는 결과를 얻지 못했습니다. 오류가 발생하지 않았지만 파일이 이동되지 않았습니다.

이 코드가 예상대로 작동하도록 어떻게 개선할 수 있습니까?

답변1

Perl 기반 코드를 사용하는 경우 rename(그런 것 같습니다) Perl 코드를 작성하는 데 쉘 문자열을 사용하지 않는 것이 가장 좋습니다. 쉘 코드로 수행할 수 있는 작업을 수행하려면 Perl 코드를 직접 사용하십시오.

이 경우 루프 내에서 다음과 같은 작업을 수행합니다.

IFS=
target="/home/test/alpha/$fldr/" rename --verbose 's/^/$ENV{target}/' "testfile - "$matchStr" - "*.txt

target여기서 대체되는 것은 명령에 대해 설정한 환경 변수의 값이므로 renamePerl 코드를 사용하여 쉽게 얻을 수 있습니다. 또한 IFS를 빈 문자열로 설정하면 단어 분리가 비활성화되므로 $matchStr와일드카드를 안전하게 사용할 수 있습니다. (그러나 여기서는 아마도 필요하지 않을 것입니다. 따옴표가 없는 항목만 사용할 수 $matchStr있으며 의 결과이기 때문에 단어 분리에 대해 걱정할 필요가 없지만 IFS=" " read -r탭이 포함된 경우는 거의 없습니다...)

답변2

주로 효과가 없고 오류가 없는 이유에 초점을 맞추려면 다음과 같이 하십시오.

perlExp=\'s/^/\\/home\\/test\\/alpha\\/"$fldr"\\//\'
rename --verbose "$perlExp" ${fullmatchStr}

perlExp작은따옴표가 포함된 문자열에 할당 되고 전달됩니다 perl. 's/^/\/home\/test\/alpha\/...\//'이는 단일 문자열 상수를 포함하는 유효한 Perl 표현식입니다. 아무런 효과가 없으므로 rename변경 사항이 적용되지 않습니다.

즉, 쉘 변수에 포함된 따옴표는 단지 ​​데이터일 뿐이며 쉘은 변수 확장 결과를 다시 구문 분석하지 않습니다. 따라서 값에 추가 따옴표를 추가하면 안 됩니다. foo="'foo bar'"; echo "$foo"및 인쇄와 동일한 작업을 수행하는 방법을 고려하십시오 .echo "'foo bar'"'foo bar'


또 다른 질문으로, fullmatchStr=testfile\ -\ "$matchStr"\ -\ *.txt변수가 문자열로 설정되고 testfile - ... - *.txt공백과 glob 문자가 모두 포함된 경우 따옴표 없이 확장하면 먼저 testfile, -, 로 분할 ....다음 *.txtglob이 확장되어 파일 이름으로 끝나는 모든 항목과 일치합니다 .txt. 이 문제를 해결하려면 IFS빈 문자열로 설정하여 단어 분리를 비활성화 해야 합니다 .

Perl을 사용하면 s///다양한 문자를 구분 기호로 사용할 수도 있는데, 이는 기울어진 이쑤시개 효과를 방지하므로 경로에 더 좋을 수 있습니다.


따라서 다음과 같이 작동할 수 있습니다.

#!/bin/bash

# disable word splitting in the script due to the unquoted expansion
IFS=

# the IFS assignment below only applies to 'read'
while IFS=" " read -r fldr matchStr
do
    perlExp='s#^#/tmp/test/alpha/'"$fldr"'/#'
    fullmatchStr="testfile - $matchStr - *.txt"
    rename --verbose "$perlExp" ${fullmatchStr}
done < test.dat

하지만 솔직히,Mulu의 솔루션환경을 통해 문자열을 전달하는 방법은 코드에 데이터를 삽입할 때 필요한 주의와 2단계 인용을 피하고 특히 파일에서 Perl로 가져온 디렉터리 이름이 무엇인지 신경 쓸 필요가 없기 때문에 더 좋습니다. 특수 문자가 포함되어 있습니다.

관련 정보