Renommez plusieurs fichiers avec mv pour changer l'extension

27

Je veux renommer des fichiers pour changer leur extension, en cherchant efficacement à accomplir

mv *.txt *.tsv

Mais en faisant cela, je reçois:

* .tsv n'est pas un répertoire

Je trouve un peu étrange que les 10 premiers résultats de Google mvsoient affichés comme ça.

Sander Van der Zeeuw
la source
Vous devez expliquer comment vous souhaitez renommer ces fichiers exactement.
Totor
1
Eh bien, j'ai des fichiers avec une extension * .gappedPeak et je veux les renommer en * .bed. ils sont tous dans le même répertoire, cela devrait être très simple mais une boucle complète était nécessaire pour que je réussisse
Sander Van der Zeeuw
Ce que vous avez trouvé sur Google s'applique à MSDOS, je pense.
tiktak
La solution alternative de @devnull est vraiment utile
lucasvw

Réponses:

22

Lorsque vous exécutez la commande:

mv *.txt *.tsv

le shell, supposons bash, développe les caractères génériques s'il y a des fichiers correspondants (y compris des répertoires). La liste des fichiers est passée au programme, ici mv. Si aucune correspondance n'est trouvée, la version non étendue est transmise.

Encore une fois: le shell étend les motifs, pas le programme.


Beaucoup d'exemples est peut-être le meilleur moyen, alors c'est parti:

Exemple 1:

$ ls
file1.txt file2.txt

$ mv *.txt *.tsv

Maintenant, ce qui se passe sur la mvligne, c'est que le shell se développe *.txtdans les fichiers correspondants. Comme il n'y a aucun *.tsvfichier qui n'est pas modifié.

La mvcommande est appelée avec deux arguments spéciaux :

  • argc: Nombre d'arguments, y compris le programme.
  • argv: Un tableau d'arguments, y compris le programme comme première entrée.

Dans l'exemple ci-dessus, ce serait:

 argc = 4
 argv[0] = mv
 argv[1] = file1.txt
 argv[2] = file2.txt
 argv[3] = *.tsv

Le mvprogramme vérifie si le dernier argument,, *.tsvest un répertoire. Comme il ne l'est pas, le programme ne peut pas continuer car il n'est pas conçu pour concaténer des fichiers. (Déplacez généralement tous les fichiers en un seul.) Ni créez de répertoires sur un coup de tête.

En conséquence, il abandonne et signale l'erreur:

mv: target ‘*.tsv’ is not a directory

Exemple 2:

Maintenant, si vous dites à la place:

$ mv *1.txt *.tsv

La mvcommande est exécutée avec:

 argc = 3
 argv[0] = mv
 argv[1] = file1.txt
 argv[2] = *.tsv

Maintenant, encore une fois, mvvérifiez s'il *.tsvexiste. Comme ce n'est pas le cas, le fichier file1.txtest déplacé vers *.tsv. C'est-à-dire: le fichier est renommé *.tsvavec l'astérisque et tout.

$ mv *1.txt *.tsv
‘file1.txt’ -> ‘*.tsv’

$ ls
file2.txt *.tsv

Exemple 3:

Si vous avez plutôt dit:

$ mkdir *.tsv
$ mv *.txt *.tsv

La mvcommande est exécutée avec:

 argc = 3
 argv[0] = mv
 argv[1] = file1.txt
 argv[1] = file2.txt
 argv[2] = *.tsv

Comme c'est *.tsvmaintenant un répertoire, les fichiers finissent par y être déplacés.


Maintenant: en utilisant des commandes comme some_command *.tsvlorsque l'intention est de conserver le caractère générique, il faut toujours le citer. En citant, vous empêchez les caractères génériques d'être étendus s'il doit y avoir des correspondances. Par exemple, dites mkdir "*.tsv".

Exemple 4:

L'extension peut encore être visualisée si vous faites par exemple:

$ ls
file1.txt file2.txt

$ mkdir *.txt
mkdir: cannot create directory ‘file1.txt’: File exists
mkdir: cannot create directory ‘file2.txt’: File exists

Exemple 5:

Maintenant: la mvcommande peut fonctionner et fonctionne sur plusieurs fichiers. Mais s'il y en a plus de deux, le dernier doit être un répertoire cible. (En option, vous pouvez utiliser l' -t TARGET_DIRoption, au moins pour GNU mv.)

Donc ça va:

$ ls -F
b1.tsv  b2.tsv  f1.txt  f2.txt  f3.txt  foo/

$ mv *.txt *.tsv foo

Ici mvserait appelé avec:

 argc = 7
 argv[0] = mv
 argv[1] = b1.tsv
 argv[2] = b2.tsv
 argv[3] = f1.txt
 argv[4] = f2.txt
 argv[5] = f3.txt
 argv[6] = foo

et tous les fichiers se retrouvent dans le répertoire foo.


Quant à vos liens. Vous en avez fourni un (dans un commentaire), où il mvn'est pas du tout mentionné, mais rename. Si vous avez plus de liens que vous pourriez partager. Ainsi que pour les pages de manuel où vous affirmez que cela est exprimé.

Runium
la source
30

Je sais que cela ne répond pas à votre question, mais au cas où vous cherchiez une autre façon de renommer les fichiers par rapport à votre boucle de contournement, pourquoi ne pas utiliser find? J'ai utilisé cette commande plusieurs fois pour remplacer les extensions de fichier dans les grands répertoires par des centaines de milliers de fichiers. Cela devrait fonctionner sur tout système compatible POSIX:

find . -name "*.gappedPeak" -exec sh -c 'mv "$1" "${1%.gappedPeak}.bed"' _ {} \;

Répartition des commandes:

  1. ' .' => chemin de recherche commençant par le répertoire courant marqué par '. "

  2. -name=> définir le nom de la correspondance de recherche (dans ce cas, tous les fichiers se terminant par .gappedPeak)

  3. -exec => exécuter la commande suivante à chaque match

  4. sh -c => 'exec' crée un environnement shell indépendant pour chaque correspondance

  5. mv "$1" "${1%.gappedPeak}.bed"=> mvpremière variable (désignée par $ 1 ), qui est le nom du fichier actuel, au nouveau nom. Ici, je fais une correspondance de sous-chaîne et je la supprime; prenez donc à nouveau la première var, $ 1 et utilisez %pour supprimer .gappedPeakde la chaîne. Le .bedà la fin concatène simplement la variable restante, qui est maintenant juste , avec , la création du nouveau nom de fichier.test#.bedtest#.bed

  6. Le trait de soulignement est un espace réservé pour 0 $

  7. Le {}est remplacé par chaque *.gappedPeaknom de fichier ( ) trouvé par la findcommande et devient $ 1 pour la shcommande.

  8. \;marque la fin de la -execcommande. Vous pouvez également utiliser ';'ou ";".

Exemple:

[user@before]# ls -lh
total 0
-rw-r--r--. 1 root root 0 Jan 26 11:40 test1.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test2.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test3.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test4.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test5.gappedPeak

[user@after]# ls -lh
total 0
-rw-r--r--. 1 root root 0 Jan 26 11:40 test1.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test2.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test3.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test4.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test5.bed
devnull
la source
Pourriez-vous parcourir la commande décrivant ce que fait chaque partie?
user1717828
Je suis désolé, j'ai mis à jour mon message. J'espère que ça t'as aidé.
devnull
1
Super merci! Avec un guide bash et votre explication, j'ai pu tout obtenir.
user1717828
Aucun problème. Heureux que ça aide.
devnull
Fonctionne comme un charme. Voici un autre exemple simplifié pour file.abc-> blub.xyzdans plusieurs sous-répertoires:find . -name "file.abc" -exec sh -c 'mv "$1" "$(dirname $1)/blub.xyz"' _ {} \;
Mahn
9

mv *.txt *.tsvne fonctionne pas; mvne peut renommer qu'un seul fichier à la fois. Vous avez soit mal compris les explications, soit elles sont fausses.

mmvet renamepeut renommer plusieurs fichiers à la fois. Mais il existe deux versions de renamearound qui sont appelées différemment. Il devrait y avoir beaucoup de questions à ce sujet ici.

Hauke ​​Laging
la source
1
cyberciti.biz/tips/… Le premier hit sur google dit que le mv devrait fonctionner aussi bien c'est pourquoi je l'ai trouvé si étrange
Sander Van der Zeeuw
3
@SanderVanderZeeuw Je ne sais pas ce que vous y lisez. Les exemples utilisent rename, non mv.
Hauke ​​Laging du
1
désolé d'avoir collé le mauvais HTML. C'était celui udemy.com/blog/rename-a-file-in-linux
Sander Van der Zeeuw
3
@SanderVanderZeeuw Embarrassing. Ces gens offrent des cours? Malheureusement, je n'y vois aucune possibilité de contact. Vous pouvez facilement vérifier si cela fonctionne. Mais dans mv *.txt *.tsv mvne voit (généralement) pas *.txtou *.tsvmais les noms de fichiers développés par shell. Le nombre de fichiers dans lesquels ces caractères génériques s'étendent serait "aléatoire". La seule situation où cela fonctionne est s'il existe un fichier avec le nom *.txtqui doit être renommé (littéralement) *.tsv(sans citer l' bashoption nullglobne doit pas être définie).
Hauke ​​Laging du
1
Ou pire. Si l'on a un fichier nommé par exemple foo.txtet un nommé baz.tsvle mv *.txt *.tsvremplacera le .tsvfichier existant ...
Runium
4

Par exemple, si vous avez des fichiers asd.txtet qwe.txtdans le répertoire lorsque vous exécutez la commande mv *.txt *.tsv, il essaie de déplacer ces deux fichiers dans un répertoire nommé *.tsv. Parce qu'il n'y a pas un tel répertoire, il donne une erreur.

Esref
la source
Merci, cela clarifie partiellement. Pourtant, je trouve étrange de voir autant de pages de manuel où ils prétendent que mv devrait fonctionner sur plusieurs.
Sander Van der Zeeuw
2

rename(1)

renameest un script Perl de Larry Wall, le créateur de Perl. Il prend une expression rationnelle Perl et fonctionne sur le nom de fichier.

rename 's/\.txt$/.tsv/' *.txt

Installation

Debian / Ubuntu

Si vous devez installer renamesur Debian / Ubuntu, vous pouvez le faire

sudo apt install rename
Evan Carroll
la source
0

Une autre option à considérer consiste à utiliser:

cp -p *.txt *.tsv
rm -f *.txt
  • La première ligne copie tous les *.txtfichiers dans *.tsvtout en préservant leurs attributs à l'aide de l' -pindicateur.
  • La deuxième ligne supprime tous les fichiers avec le motif *.txt
  • Cette méthode nécessite que vous ayez suffisamment d'espace disque pour contenir temporairement les fichiers source et cible
  • Cette méthode est plus lente que celle mvqui modifie simplement le répertoire / filename sans déplacer réellement les fichiers à moins qu'ils ne se trouvent sur une partition différente.
WinEunuuchs2Unix
la source