Lorsque je fais rm *.old.*
sur la ligne de commande, il est supprimé correctement, mais lorsque je le fais dans la partie suivante de mon script, il ne récupère pas tous les fichiers *.old.*
.
Quel est le problème dans mon 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
Si je comprends ce que vous faites (supprimez tous les fichiers avec le suffixe .old
et copiez tous les fichiers existants avec un suffixe .old
), vous pouvez utiliser find à la place:
#!/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
arrête la commande de recherche dans les sous-répertoires, -type f
recherche uniquement les fichiers normaux; -printf
crée des messages (%P
est le nom du fichier trouvé). Le -exec cp
appelle la fonction de copie et '{}'
est le nom du fichier.
Il existe différents points d'échec possibles dans votre script. Tout d'abord, rm *.old*
utilisera globbing pour créer une liste de tous les fichiers correspondants et pouvant traiter des noms de fichiers contenant des espaces. Cependant, votre script assigne une variable à chaque résultat du glob et le fait sans les citer. Cela se brisera si vos noms de fichiers contiennent des espaces. Par exemple:
$ 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'
Comme vous pouvez le constater, la boucle a échoué pour le fichier avec des espaces dans son nom. Pour le faire correctement, vous devez citer la variable:
$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'
Le même problème concerne à peu près toutes les utilisations de $i
dans votre script. Vous devriez toujours citer vos variables .
Le problème suivant est que vous semblez vous attendre à ce que *.old.*
corresponde aux fichiers portant l'extension .old
. Ce n'est pas. Il correspond à "0 caractères ou plus" (*
), puis à un .
, puis à "ancien", puis à un autre .
et ensuite "0 ou plusieurs caractères à nouveau". Cela signifie que ne correspondra pas à quelque chose comme file.old
, mais seulement à quelque chose comme `file.old.foo:
$ ls
file.old file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo
Donc, aucun match ne correspond à file.old
. Dans tous les cas, votre script est beaucoup plus complexe que nécessaire. Essayez celui-ci à la place:
#!/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
Notez que j'ai ajouté -v
aux instructions rm
et cpwhich does the same thing as what you were doing with your
echo`.
Ce n'est pas parfait, car lorsque vous trouvez, par exemple, file.old
, celui-ci sera supprimé et que, plus tard, le script tentera de le copier et échouera, car le fichier n'existe plus. Cependant, vous n'avez pas expliqué ce que votre script tente réellement de faire, donc je ne peux pas résoudre ce problème pour vous à moins que vous ne nous disiez ce que vous essayez vraiment d'accomplir.
Si vous voulez i) supprimer tous les fichiers avec l’extension .old
et ii) ajouter l’extension .old
aux fichiers existants qui ne l’ont pas, il vous suffit de:
#!/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
Les seuls cas où rm $oldfile
pourrait échouer sont ceux où votre nom de fichier contient un caractère de IFS
(espace, tabulation, nouvelle ligne) ou n’importe quel caractère global (*
, ?
, []
).
Si un caractère de IFS
est présent, le shell procédera au fractionnement de Word en fonction de la présence de caractères globaux, élargissant le chemin du nom du chemin de la variable.
Ainsi, par exemple, si le nom du fichier est foo bar.old.
, la variable oldfile
contiendrait foo bar.old.
.
Quand tu fais:
rm $oldfile
Dans un premier temps, Shell divise le développement de oldfile
de l'espace en deux mots, foo
et bar.old.
. Donc la commande devient:
rm foo bar.old.
ce qui conduirait évidemment à un résultat inattendu. Soit dit en passant, si vous avez des opérateurs globants (*
, ?
, []
) dans l’extension, l’extension du chemin sera également effectuée.
Vous devez citer les variables pour obtenir le résultat souhaité:
rm "$oldfile"
Désormais, aucun fractionnement de mot ou extension de nom de chemin ne serait effectué. Par conséquent, vous devriez obtenir le résultat souhaité, c'est-à-dire que le fichier souhaité serait supprimé. Si un nom de fichier commence par -
, alors:
rm -- "$oldfile"
Vous vous demandez peut-être pourquoi nous n’avons pas besoin de citer les variables lorsqu’elles sont utilisées dans [[
, la raison étant [[
est un mot clé bash
et il gère le développement des variables en interne en conservant le littéral de développement.
Maintenant, quelques points:
Vous devez rediriger STDERR (exec 2>errorfile
) avant la commande rm
sinon le test [[ -s errorfile ]]
donnerait de faux positifs.
Vous avez utilisé [ -s $errorfile ]
, vous utilisez une extension de variable $errorfile
, qui serait NUL étant donné que la variable errorfile
n'est définie nulle part. Peut-être que vous vouliez dire, juste [ -s errorfile ]
, basé sur la redirection STDERR
Si la variable errorfile
est définie, si vous utilisez [ -s $errorfile ]
, les cas susmentionnés de IFS
et de globbing mentionnés ci-dessus ne sont pas concernés car, contrairement à [[
, [
n'est pas géré en interne par bash
name__.
Dans la dernière partie du script, vous essayez de cp
le fichier déjà supprimé (encore une fois sans citer la variable), cela n'a aucun sens, vous devriez vérifier ce mandrin et apporter les corrections nécessaires en fonction de votre cible.