rm funziona sulla riga di comando ma non nello script


11

Quando lo faccio rm *.old.*dalla riga di comando, rimuove correttamente, ma quando lo faccio nella seguente parte del mio script, non contiene tutti i *.old.*file.

Cosa c'è di sbagliato nel mio script 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

Risposte:


4

Se capisco cosa stai facendo (elimina qualsiasi file con il .oldsuffisso e crea una copia di tutti i file esistenti con un .oldsuffisso), puoi invece usare 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 0interrompe la ricerca del comando find nelle sottodirectory, -type fcerca solo i file regolari; -printfcrea messaggi ( %Pè il nome file trovato). La -exec cpchiama la funzione di copia e '{}'è il nome del file


14

Ci sono vari possibili punti di errore nel tuo script. Prima di tutto, rm *.old*utilizzerà globbing per creare un elenco di tutti i file corrispondenti e che può gestire nomi di file contenenti spazi bianchi. Il tuo script, tuttavia, assegna una variabile a ciascun risultato del glob e lo fa senza virgolette. Ciò si interromperà se i nomi dei file contengono spazi bianchi. Per esempio:

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

Come puoi vedere, il ciclo non è riuscito per il file con spazi nel suo nome. Per farlo correttamente, dovresti citare la variabile:

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

Lo stesso problema si applica praticamente a ogni uso del $ituo script. Dovresti sempre citare le tue variabili .

Il prossimo possibile problema è che ti aspetti che *.old.*corrisponda ai file con l'estensione .old. Non Corrisponde a "0 o più caratteri" ( *), quindi a ., quindi "vecchio", quindi un altro .e poi "0 o più caratteri". Ciò significa che non corrisponderà a qualcosa di simile file.old, ma solo a qualcosa come `file.old.foo:

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

Quindi, nessun nemico file.old. In ogni caso, la tua sceneggiatura è molto più complessa del necessario. Prova questo invece:

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

Si noti che ho aggiunto -valle istruzioni rme cp which does the same thing as what you were doing with yourecho`.

Questo non è perfetto da quando, ad esempio, si trova file.oldche verrà rimosso e, in seguito, lo script proverà a copiarlo e fallirà poiché il file non esiste più. Tuttavia, non hai spiegato cosa sta effettivamente tentando di fare lo script, quindi non posso risolvere il problema per te a meno che tu non ci dica cosa stai veramente cercando di realizzare.

Se quello che vuoi è i) rimuovere tutti i file con l' .oldestensione e ii) aggiungere l' .oldestensione a tutti i file esistenti che non ce l'hanno, tutto ciò di cui hai veramente bisogno è:

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

Sto cercando di eseguire il backup dei file su file.old ma allo stesso tempo rm tutti i file che terminano in .old.old o .old.old.old o .old.old.old ecc. Sulla riga di comando che uso rm *.old.*che rimuove questi file ma non il file di backup file.old. Sto provando a farlo nella mia sceneggiatura. Grazie
Don

1
@Don modifica la tua domanda e spiegala in modo più dettagliato. Includi i nomi dei file di esempio e cosa vorresti succedere loro dopo l'esecuzione dello script. Idealmente, entra in chat e ping me lì in modo che possiamo discuterne.
Terdon,

8

Gli unici casi rm $oldfilepotevano non sono quando il nome del file contiene qualsiasi carattere di IFS(spazio, tabulazione, nuova riga) o qualsiasi carattere glob ( *, ?, []).

Se IFSè presente un carattere di, la shell eseguirà la suddivisione delle parole e in base alla presenza di caratteri globbing sull'espansione del percorso sull'espansione variabile.

Quindi, ad esempio, se il nome del file è foo bar.old., la variabile oldfileconterrebbe foo bar.old..

Quando lo fai:

rm $oldfile

shell inizialmente divide l'espansione di oldfileon space in due parole, fooe bar.old.. Quindi il comando diventa:

rm foo bar.old.

che ovviamente porterebbe a risultati inaspettati. A proposito, se si dispone di tutti gli operatori globbing ( *, ?, []) per l'espansione, quindi espansione di percorso sarebbe fatto troppo.

Devi citare le variabili per ottenere il risultato desiderato:

rm "$oldfile"

Ora, nessuna suddivisione delle parole o l'espansione del percorso verrà eseguita, quindi dovresti ottenere il risultato desiderato, cioè il file desiderato verrebbe rimosso. Se si verifica un nome di file, iniziare con -:

rm -- "$oldfile"

Potresti chiederti, perché non abbiamo bisogno di citare le variabili quando vengono utilizzate all'interno [[, il motivo [[è una bashparola chiave e gestisce l'espansione delle variabili internamente mantenendo l'espansione letterale.


Ora, un paio di punti:

  • È necessario reindirizzare STDERR ( exec 2>errorfile) prima che il rmcomando altrimenti [[ -s errorfile ]]verifichi falsi positivi

  • Hai usato [ -s $errorfile ], stai usando un'espansione variabile $errorfile, che sarebbe NUL dato che la errorfilevariabile non è definita da nessuna parte. Forse intendevi semplicemente [ -s errorfile ]basarti sul reindirizzamento STDERR

  • Se la variabile errorfileviene definita, durante l'utilizzo [ -s $errorfile ], si soffocerebbe di nuovo sui casi di cui sopra IFSe globbing perché a differenza di [[, [non è gestita internamente dabash

  • Nella parte successiva dello script, stai provando cpil file già rimosso (di nuovo senza citare la variabile), questo non ha alcun senso, dovresti controllare quel mandrino e apportare le correzioni necessarie in base al tuo obiettivo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.