it-swarm.com.de

rm funktioniert auf der Kommandozeile, aber nicht im Skript

Wenn ich rm *.old.* in der Befehlszeile mache, wird es korrekt entfernt, aber wenn ich es im folgenden Teil meines Skripts mache, werden nicht alle *.old.* -Dateien gespeichert.

Was ist falsch in meinem Bash-Skript:

 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
10
Don

Wenn ich verstehe, was Sie tun (löschen Sie alle Dateien mit dem Suffix .old und kopieren Sie alle vorhandenen Dateien mit dem Suffix .old), können Sie stattdessen find verwenden:

#!/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 stoppt den Suchbefehl und sucht in Unterverzeichnissen. -type f sucht nur nach regulären Dateien. -printf erstellt Nachrichten (%P ist der gefundene Dateiname). Der -exec cp ruft die Kopierfunktion auf und '{}' ist der Dateiname

3
Nick Sillito

Es gibt verschiedene mögliche Fehlerpunkte in Ihrem Skript. Zunächst verwendet rm *.old*globbing , um eine Liste aller übereinstimmenden Dateien zu erstellen, und das kann mit Dateinamen umgehen, die Leerzeichen enthalten. Ihr Skript weist jedoch jedem Ergebnis des Glob eine Variable zu, und das ohne Anführungszeichen. Das wird kaputt gehen, wenn Ihre Dateinamen Leerzeichen enthalten. Zum Beispiel:

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

Wie Sie sehen, ist die Schleife für die Datei mit Leerzeichen im Namen fehlgeschlagen. Um es richtig zu machen, müssten Sie die Variable in Anführungszeichen setzen:

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

Das gleiche Problem gilt für so ziemlich jede Verwendung von $i in Ihrem Skript. Sie sollten immer Ihre Variablen angeben .

Das nächste mögliche Problem ist, dass Sie anscheinend erwarten, dass *.old.* Dateien mit der Erweiterung .old entspricht. Das tut es nicht. Es entspricht "0 oder mehr Zeichen" (*), dann einem ., dann "alt", dann einem anderen . und dann "0 oder mehr Zeichen erneut". Dies bedeutet, dass es nicht mit etwas wie file.old übereinstimmt , sondern nur mit etwas wie `file.old.foo:

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

Also, kein Match Feind file.old. In jedem Fall ist Ihr Skript weitaus komplexer als nötig. Versuchen Sie dies stattdessen:

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

Beachten Sie, dass ich -v zu den rm- und cpwhich does the same thing as what you were doing with yourecho-Anweisungen hinzugefügt habe.

Dies ist nicht perfekt, da beispielsweise file.old gefunden wird, das entfernt wird, und das Skript später versucht, es zu kopieren, und fehlschlägt, da die Datei nicht mehr vorhanden ist. Sie haben jedoch nicht erklärt, was Ihr Skript tatsächlich versucht, sodass ich das nicht für Sie beheben kann, es sei denn, Sie teilen uns mit, was Sie wirklich erreichen möchten.

Wenn Sie möchten, dass i) alle Dateien mit der Erweiterung .old entfernt und ii) die Erweiterung .old zu allen vorhandenen Dateien hinzugefügt werden, die diese Erweiterung nicht haben, ist alles, was Sie wirklich benötigen:

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

Die einzigen Fälle, in denen rm $oldfile fehlschlagen könnte, sind, wenn Ihr Dateiname ein Zeichen von IFS (Leerzeichen, Tabulator, Zeilenvorschub) oder ein Glob-Zeichen (*, ?, []) enthält.

Wenn ein Zeichen von IFS vorhanden ist, führt die Shell eine Word-Aufteilung durch und basiert auf dem Vorhandensein einer Pfadnamenerweiterung für die Variablenerweiterung.

Wenn der Dateiname beispielsweise foo bar.old. lautet, enthält die Variable oldfilefoo bar.old..

Wenn Sie das tun:

rm $oldfile

Shell teilt die Erweiterung von oldfile im Leerzeichen zunächst in zwei Wörter auf, foo und bar.old.. So wird der Befehl:

rm foo bar.old.

was natürlich zu unerwartetem Ergebnis führen würde. Übrigens, wenn Sie globale Operatoren (*, ?, []) in der Erweiterung haben, wird die Pfadnamenerweiterung ebenfalls durchgeführt.

Sie müssen die Variablen in Anführungszeichen setzen, um das gewünschte Ergebnis zu erhalten:

rm "$oldfile"

Jetzt würde keine Wortteilung oder Pfadnamenerweiterung durchgeführt, daher sollten Sie das gewünschte Ergebnis erhalten, d. H. Die gewünschte Datei würde entfernt. Wenn ein Dateiname zufällig mit - beginnt, gehen Sie wie folgt vor:

rm -- "$oldfile"

Sie könnten sich fragen, warum wir keine Variablen in [[ zitieren müssen. Der Grund dafür ist, dass [[ ein bash Schlüsselwort ist und die Variablenerweiterung intern behandelt, wobei das Erweiterungsliteral beibehalten wird.


Nun ein paar Punkte:

  • Sie sollten STDERR (exec 2>errorfile) umleiten, bevor Sie den Befehl rm ausführen, da der [[ -s errorfile ]]-Test sonst falsche positive Ergebnisse liefert

  • Sie haben [ -s $errorfile ] verwendet, Sie verwenden eine Variablenerweiterung $errorfile, die NUL wäre, wenn errorfile Variable nirgends definiert ist. Vielleicht hast du nur [ -s errorfile ] gemeint, basierend auf der STDERR-Umleitung

  • Wenn die Variable errorfile bei Verwendung von [ -s $errorfile ] definiert ist, würde sie die oben genannten Fälle von IFS und Globbing erneut abwürgen, da [[ im Gegensatz zu [ nicht intern von bash behandelt wird.

  • Im späteren Teil des Skripts versuchen Sie, die bereits entfernte Datei zu cp (erneut ohne Anführungszeichen für die Variable). Dies macht keinen Sinn. Sie sollten dieses Spannfutter überprüfen und die erforderlichen Korrekturen basierend auf Ihrem Ziel vornehmen.

8
heemayl