rm komut satırında çalışır ancak komut dosyasında çalışmaz


11

Ben ne zaman rm *.old.*komut satırında doğru kaldırır ama Senaryomun aşağıdaki kısmında bunu yaparken, tüm rm gelmez *.old.*dosyaları.

Benim bash senaryomda yanlış olan şey:

 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

Yanıtlar:


4

Ne yaptığınızı anlarsam (sonekle birlikte tüm dosyaları siler ve .oldsonekle birlikte mevcut dosyaların bir kopyasını çıkarırsam .old), bunun yerine find komutunu kullanabilirsiniz:

#!/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 0find dizinini alt dizinlere bakarak durdurur, -type fyalnızca normal dosyaları arar; -printfileti oluşturur ( %Pbulunan dosya adıdır). -exec cpKopyalama işlevini çağırır ve '{}'dosya adıdır


14

Komut dosyanızda çeşitli olası hata noktaları var. Her şeyden önce, eşleşen tüm dosyaların bir listesini oluşturmak rm *.old*için globbing kullanır ve bu boşluk içeren dosya adları ile başa çıkabilir. Ancak betiğiniz globun her sonucuna bir değişken atar ve bunu tırnak işareti olmadan yapar. Dosya adlarınız boşluk içeriyorsa bu bozulur. Örneğin:

$ 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'

Gördüğünüz gibi, döngü adında boşluk bulunan dosya için başarısız oldu. Doğru yapmak için değişkeni alıntılamanız gerekir:

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

Aynı sorun $i, betiğinizdeki hemen hemen her kullanım için de geçerlidir . Her zaman değişkenlerinizi belirtmelisiniz .

Bir sonraki olası sorun, *.old.*uzantıya sahip dosyalarla eşleşmesini beklediğiniz gibi görünüyor .old. Öyle değil. "0 veya daha fazla karakter" ( *), sonra a ., sonra "eski", sonra başka .ve daha sonra "0 veya daha fazla karakter" ile eşleşir . O edeceği Bu araçlar değil gibi bir şey eşleşmesi file.old, ancak `file.old.foo gibi tek bir şey:

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

Yani, maç düşmanı yok file.old. Her durumda, betiğiniz gerekenden çok daha karmaşıktır. Bunun yerine şunu deneyin:

#!/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

Ben ekledi Not -viçin rmve cp which does the same thing as what you were doing with yourecho` ifadeleri.

Örneğin, file.oldkaldırılacağını bulduğunuzda ve daha sonra komut dosyası kopyalamaya çalışacağından ve dosya artık mevcut olmadığından başarısız olacağından bu mükemmel değildir. Bununla birlikte, senaryonuzun aslında ne yapmaya çalıştığını açıklamıyorsunuz, bu yüzden gerçekten neyi başarmaya çalıştığınızı söylemediğiniz sürece bunu sizin için düzeltemem.

İstediğiniz şey i) .olduzantıya sahip tüm dosyaları kaldırmak ve ii) .olduzantıyı sahip olmayan mevcut dosyalara eklemek ise , gerçekten ihtiyacınız olan şey:

#!/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

Ben .old.old uç veya komut satırı ben kullanımı üzerinde .old.old.old vb .old.old.old veya herhangi dosyalara file.old ama aynı zamanda rm de yedekleme dosyalarına çalışıyorum rm *.old.*bu dosyaları kaldırır ancak file.old yedek dosyasını değil. Bunu senaryomda yapmaya çalışıyorum. Teşekkürler
Don

1
@Lütfen sorunuzu düzenleyin ve bunu daha ayrıntılı olarak açıklayın. Komut dosyası çalıştırıldıktan sonra örnek dosya adlarını ve bu dosyalara ne olmasını istediğinizi ekleyin. İdeal olarak, sohbete girip tartışabilmemiz için bana ping atın .
terdon

8

Sadece vakalar rm $oldfilebaşarısız olabilir dosyanızın adı herhangi bir karakter içeriyorsa olan IFS(boşluk, sekme, yeni satır) veya herhangi glob karakteri ( *, ?, []).

Herhangi bir karakteri varsa IFS, kabuk sözcük bölme işlemini gerçekleştirir ve değişken genişletme üzerinde yuvarlanan karakterlerin yol adı genişlemesine bağlı olarak çalışır.

Örneğin, dosya adı ise foo bar.old., değişken oldfileiçerir foo bar.old..

Yaptığınızda:

rm $oldfile

Kabuk ilk başta oldfileuzayın genişlemesini iki kelimeye ayırır foove bar.old.. Böylece komut şöyle olur:

rm foo bar.old.

bu da beklenmedik sonuçlara yol açacaktır. Eğer herhangi bir globbing operatörleri (varsa arada, *, ?, []genişleme), sonra yol adı genleşme çok yapılırdı.

İstenen sonucu elde etmek için değişkenleri belirtmeniz gerekir:

rm "$oldfile"

Şimdi, hiçbir kelime bölme veya yol adı genişletme yapılmaz, bu nedenle istenen sonucu almalısınız, yani istenen dosya kaldırılacaktır. Herhangi bir dosya adı başlarsa -, şunu yapın:

rm -- "$oldfile"

İçeride kullanıldığında değişkenleri neden alıntılamamız gerekmediğini sorabilirsiniz, bunun [[nedeni [[bir bashanahtar kelimedir ve genişleme gerçekliğini koruyarak dahili genişlemeyi içsel olarak işler.


Şimdi, birkaç nokta:

  • Komuttan exec 2>errorfileönce STDERR ( ) yöntemini yeniden yönlendirmelisiniz , rmaksi takdirde [[ -s errorfile ]]test yanlış pozitifler verebilir

  • Kullandığınız [ -s $errorfile ], değişken genişleme kullanıyorsunuz $errorfile, NUL verilecek olan errorfiledeğişken hiçbir yerde tanımlanmamış. Belki de sadece [ -s errorfile ]STDERR yönlendirmesine dayanmak istediniz

  • Değişken errorfiletanımlanırsa, kullanırken [ -s $errorfile ], yukarıda belirtilen vakaları IFSboğar ve globbing [[, aksine , [dahili olarak tarafından ele alınmaz.bash

  • Komut dosyasının sonraki bölümünde cp, zaten kaldırılmış dosyaya çalışıyorsunuz (yine değişkeni alıntılamadan), bu bir anlam ifade etmiyor, bu aynayı kontrol etmeli ve hedefinize göre gerekli düzeltmeleri yapmalısınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.