Évitez de remplacer les espaces lorsque vous renommez des fichiers

10

J'essaie de renommer des fichiers comme celui-ci:

for file in *;
do
mv -i "$file" "$(echo "$file" | sed -e 's/[^A-Za-z0-9._-]/_/g')";
done

Mais la sedcommande remplace tous les espaces par _.

Comment puis-je modifier la sedcommande pour y inclure des espaces avec les caractères spécifiés? J'ai essayé d'utiliser \smais ça ne marche pas ...

EDIT:
Par exemple: le fichier trip: hill, devrait être renommé:, trip_ hillmais la commande ci-dessus le fait trip__hill.

Zanna
la source
Je déplace des fichiers de Linux vers Mac. Mais pour une raison quelconque, le système Mac ne peut pas lire tous les noms de fichiers, j'essaie donc de renommer mes fichiers. L'ajout d'un espace à la commande ci-dessus ne fonctionne pas. Et vous avez raison: je veux remplacer tous les caractères par '_' sauf ceux spécifiés. Et je ne sais pas comment inclure le caractère spatial.
Veuillez noter que j'essaie de remplacer tous les caractères par «_» sauf ceux spécifiés. Il n'y a pas de modèle particulier pour les fichiers renommés, donc je ne peux pas utiliser cette commande perl.

Réponses:

11

Ne pas analyser les noms de fichiers avec sed! La sortie de echo "$file"peut ne pas être fiable.

Utilisez rename. Le 17.10, vous devez d'abord l'installer

sudo apt install rename

Ensuite:

rename -n -- 's/[^-A-Za-z0-9_ .]/_/g' *

Remarques

  • supprimer -naprès le test pour renommer réellement les fichiers
  • -- fin des options dans le cas où un fichier commence par -
  • [^-A-Za-z0-9_ .]les caractères que nous ne voulons pas remplacer - mis en -premier ou en dernier afin qu'il ne puisse pas indiquer une plage (il est traité littéralement dans ces positions).
  • Des espaces peuvent être inclus dans la classe
  • . est traité littéralement (dans d'autres contextes d'expression régulière, il représente n'importe quel caractère et doit être échappé).

Cela fonctionne également dans sed:

$ echo 'trip: hill' | sed 's/[^-A-Za-z0-9 _.]/_/g'
trip_ hill

Si j'ajoute un espace à la fin dans votre version, j'obtiens une erreur:

$ echo 'trip: hill' | sed -e 's/[^A-Za-z0-9._- ]/_/g'
sed: -e expression #1, char 22: Invalid range end

Mais -à la fin, ça marche:

$ echo 'trip: hill' | sed -e 's/[^A-Za-z0-9._ -]/_/g'
trip_ hill

Peut-être que la position du trait d'union a causé votre problème lorsque vous avez ajouté l'espace. Mais le conseil de ne pas analyser les noms de fichiers est valable!

Zanna
la source
Cette commande fonctionne merci. Vous avez également clairement identifié les erreurs dans la commande que j'ai utilisée.
2
Merci encore. Veuillez noter qu'il n'est pas nécessaire d'utiliser la barre oblique "\" dans le cas de l'utilisation de sed. Parce que cela ajoute «\» à la liste des caractères ignorés.
2
@latach C'est une barre oblique inverse, mais vous avez absolument raison. Après avoir testé et fouillé, j'ai trouvé que .c'était littéral dans une classe de caractères. TIL!
Zanna
2

Vous pouvez également simplement utiliser le shell, l'expansion des paramètres de Bash peut faire une substitution:

for f in ./* ; do
    mv "$f" "${f//[^-A-Za-z0-9._ ]/_}"
done

La double barre oblique lui dit de remplacer toutes les correspondances, à part cela, la syntaxe est simple.

ilkkachu
la source