rm은 명령 행에서는 작동하지만 스크립트에서는 작동하지 않습니다.


11

rm *.old.*명령 줄에서 수행 하면 올바르게 제거되지만 스크립트의 다음 부분에서 수행하면 모든 *.old.*파일 이 생성되지는 않습니다 .

내 bash 스크립트의 문제점 :

 for i in ./*; do
    if [[ -f $i ]];  then

        if [[ $i  ==  *.old.* ]]; then
                oldfile=$i
                echo "this file is to be removed: $oldfile"
                rm $oldfile
                exec 2>errorfile
            if [ -s $errorfile ]
            then
                echo "rm failed"
            else
                echo "removed $oldfile!"
            fi
        else
            echo "file with old extension  does not exist"
        fi

        orig=$i
        dest=$i.old
        cp $orig $dest
        echo "Copied $i"

    else
        echo "${i} is not a file"
    fi 
done

답변:


4

내가하는 일을 이해하면 ( .old접미사가 있는 파일을 삭제하고 접미사가있는 기존 파일을 복사하십시오 .old) find 대신 다음을 사용할 수 있습니다.

#!/bin/sh

find . -maxdepth 1 -name \*.old -type f -printf "deleting %P\n" -delete
find . -maxdepth 1 -type f -printf "copying %P to %P.old\n" -exec cp '{}' '{}.old' \;

-maxdepth 0하위 디렉토리에서 찾기 명령을 중지하고 -type f일반 파일 만 찾습니다. -printf메시지를 작성합니다 ( %P발견 된 파일 이름입니다). 는 -exec cp복사 함수를 호출하고 '{}'파일 이름입니다


14

스크립트에 여러 가지 가능한 실패 지점이 있습니다. 우선, rm *.old*사용 로빙 일치하는 모든 파일의 목록을 작성하고, 그 공백을 포함하는 파일 이름을 처리 할 수 있습니다. 그러나 스크립트는 glob의 각 결과에 변수를 할당하고 인용하지 않고 수행합니다. 파일 이름에 공백이 있으면 중단됩니다. 예를 들면 다음과 같습니다.

$ ls
'file name with spaces.old.txt'  file.old.txt
$ rm *.old.*   ## works: both files are deleted

$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'

보다시피, 이름에 공백이있는 파일에 대해 루프가 실패했습니다. 올바르게하려면 변수를 인용해야합니다.

$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'

동일한 문제가 $i스크립트에서 거의 모든 용도에 적용됩니다 . 당신은해야한다 항상 당신의 변수를 인용 .

다음 가능한 문제는 *.old.*확장자가 파일과 일치 할 것으로 예상된다는 것 .old입니다. 그렇지 않습니다. "0 자 이상"( *), " a .", "old", 다른 "0 자 이상"과 일치 .합니다. 이 것이 의미 없는 같은 일치 file.old하지만`file.old.foo 같은 단지 뭔가를 :

$ ls
file.old  file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo     

따라서 일치하는 적이 없습니다 file.old. 어쨌든 스크립트는 필요한 것보다 훨씬 복잡합니다. 대신 이것을 시도하십시오 :

#!/bin/bash

for i in *; do
    if [[ -f "$i" ]];  then
        if [[ "$i"  ==  *.old ]]; then
            rm -v "$i" || echo "rm failed for $i"
        else
            echo "$i doesn't have an .old extension"
        fi
        cp -v "$i" "$i".old
    else
        echo "$i is not a file"
    fi 
done

and cp echo` 문에 추가 -v했습니다 .rmwhich does the same thing as what you were doing with your

예를 들어을 (를) 찾을 때 file.old제거되고 나중에 파일이 더 이상 존재하지 않으므로 스크립트가이를 복사하려고 시도하고 실패하기 때문에 이는 완벽 하지 않습니다. 그러나 스크립트가 실제로 시도하는 것을 설명하지 않았으므로 실제로 달성하려는 것을 알려주지 않으면 해결할 수 없습니다.

원하는 경우 i) .old확장자를 가진 모든 파일을 제거 하고 ii) .old확장자가없는 기존 파일에 확장자를 추가 하면 실제로 필요한 것은 다음과 같습니다.

#!/bin/bash

for i in *.old; do
    if [[ -f "$i" ]]; then
        rm -v "$i" || echo "rm failed for $i"
    else
        echo "$i is not a file"
    fi 
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
    if [[ -f "$i" ]]; then
        ## the -v makes cp report copied files
        cp -v "$i" "$i".old
    fi
done

파일을 file.old에 백업하려고하지만 동시에 .old.old 또는 .old.old.old 또는 .old.old.old 등으로 끝나는 모든 파일을 rm합니다. 명령 줄에서 rm *.old.*이러한 파일을 제거합니다. file.old 백업 파일은 아닙니다. 내 스크립트에서 그렇게하려고합니다. 감사합니다
Don

1
@Don 귀하의 질문을 편집하고 더 자세히 설명하십시오. 예제 파일 이름과 스크립트가 실행 된 후 어떤 파일이 발생하고 싶은지 포함하십시오. 이상적으로는 대화나눌 수 있도록 대화나눌 수 있습니다.
terdon

8

유일한 경우 rm $oldfile실패 할 수 있습니다 귀하의 파일 이름의 모든 문자가 포함 된 경우입니다 IFS(공백, 탭, 줄 바꿈) 또는 글로브 문자 ( *, ?, []).

의 문자 IFS가 있으면 쉘은 단어 분할을 수행하고 변수 확장에서 globbing 문자 pathname 확장의 존재 여부를 기반으로합니다.

예를 들어 파일 이름이 인 foo bar.old.경우 variable oldfile은을 포함합니다 foo bar.old..

할 때 :

rm $oldfile

첫번째 분할에서의 확장 쉘 oldfile두 단어로 공간을, foobar.old.. 따라서 명령은 다음과 같습니다.

rm foo bar.old.

예상치 못한 결과로 이어질 것입니다. 어떤 글 로빙 사업자 (있는 경우 그건 그렇고, *, ?, []확장 인치), 다음 경로 확장도 할 수있다.

원하는 결과를 얻으려면 변수를 인용해야합니다.

rm "$oldfile"

이제 단어 분할이나 경로 이름 확장이 수행되지 않으므로 원하는 결과를 얻을 수 있습니다. 즉 원하는 파일이 제거됩니다. 파일 이름이로 시작 -하면 다음을 수행하십시오.

rm -- "$oldfile"

내부 [[에서 사용될 때 변수를 인용 할 필요가없는 이유를 물을 수 있습니다 . 이유 [[bash키워드이므로 확장 리터럴을 유지하면서 변수 확장을 내부적으로 처리합니다.


이제 몇 가지 사항이 있습니다.

  • 당신은 STDERR을 (리디렉션해야 exec 2>errorfile전과) rm, 그렇지 않으면 명령 [[ -s errorfile ]]테스트가 잘못된 반응을 줄 것

  • 당신이 사용하고 [ -s $errorfile ]당신이 변수 확장 사용하고, $errorfileNUL 주어진 것, errorfile어디서나 정의되지 않은 변수를. 아마도 [ -s errorfile ]STDERR 리디렉션을 기반으로 한 것입니다.

  • 변수가 있다면 errorfile정의되어 사용하는 동안 [ -s $errorfile ], 다시의 언급 경우 위에 질식 것 IFS과는 달리하기 때문에 대체 (globbing) [[, [내부적으로 처리되지 않는다bash

  • 스크립트의 후반부 cp에서 이미 변수를 인용하지 않고 이미 제거 된 파일을 시도하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.