나는 데이터 파일에 지정된 매개변수를 기반으로 정적 폴더의 파일을 이동하는 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를 읽었습니다.
- mv: 셀 스크립트에 해당 파일이나 디렉터리가 없습니다.
- https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables
- 공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?
하지만 여전히 원하는 결과를 얻지 못했습니다. 오류가 발생하지 않았지만 파일이 이동되지 않았습니다.
이 코드가 예상대로 작동하도록 어떻게 개선할 수 있습니까?
답변1
Perl 기반 코드를 사용하는 경우 rename
(그런 것 같습니다) Perl 코드를 작성하는 데 쉘 문자열을 사용하지 않는 것이 가장 좋습니다. 쉘 코드로 수행할 수 있는 작업을 수행하려면 Perl 코드를 직접 사용하십시오.
이 경우 루프 내에서 다음과 같은 작업을 수행합니다.
IFS=
target="/home/test/alpha/$fldr/" rename --verbose 's/^/$ENV{target}/' "testfile - "$matchStr" - "*.txt
target
여기서 대체되는 것은 명령에 대해 설정한 환경 변수의 값이므로 rename
Perl 코드를 사용하여 쉽게 얻을 수 있습니다. 또한 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
, -
, 로 분할 ...
된 .
다음 *.txt
glob이 확장되어 파일 이름으로 끝나는 모든 항목과 일치합니다 .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로 가져온 디렉터리 이름이 무엇인지 신경 쓸 필요가 없기 때문에 더 좋습니다. 특수 문자가 포함되어 있습니다.