web-dev-qa-db-fra.com

Comment envoyer la sortie de ls à mv?

Je sais que vous pouvez le faire avec un find, mais existe-t-il un moyen d'envoyer la sortie de ls à mv dans la ligne de commande unix?

38
Christopher

Une façon est avec des backticks:

mv `ls *.boo` subdir

Edit: cependant, ceci est fragile et not recommandé - voir la réponse de @ lhunath pour plus de détails explications et recommandations.

21
Alex Martelli

ls est un outil utilisé pour AFFICHER des statistiques sur les noms de fichiers dans un répertoire.

Ce n'est pas un outil que vous devez utiliser pour les énumérer et les passer à un autre outil pour les utiliser là-bas. Analyser ls est presque toujours la mauvaise chose à faire, et il est buggé à bien des égards.

Pour un document détaillé sur la méchanceté de l'analyse ls, que vous devriez vraiment aller lire, consultez: http://mywiki.wooledge.org/ParsingLs

Au lieu de cela, vous devez utiliser des globes ou rechercher, selon ce que vous essayez exactement de réaliser:

mv * /foo
find . -exec mv {} /foo \;

La principale source d'erreur de l'analyse ls est que ls sauvegarde tous les noms de fichiers dans une seule chaîne de sortie, et il n'y a aucun moyen de distinguer les noms de fichiers de là. Pour tout ce que vous savez, la sortie entière de ls pourrait être un seul nom de fichier!

La source secondaire de la méchanceté de l'analyse ls vient de la manière brisée dont la moitié du monde utilise bash. Ils pensent que for fait comme par magie ce qu'ils voudraient faire quand ils font quelque chose comme:

for file in `ls`  # Never do this!
for file in $(ls) # Exactly the same thing.

for est une commande bash qui itère sur les arguments. Et $(ls) prend la sortie de ls et la coupe en arguments partout où il y a spaces, newlines ou tabs. Ce qui signifie essentiellement que vous itérez sur les mots , pas sur les noms de fichiers . Pire encore, vous demandez à bask de prendre chacun de ces mots de nom de fichier mutilés, puis de les traiter comme des globes pouvant correspondre aux noms de fichiers du répertoire actuel. Donc, si vous avez un nom de fichier qui contient un mot qui se trouve être un glob qui correspond à d'autres noms de fichiers dans le répertoire actuel, ce mot disparaîtra et tous ces noms de fichiers correspondants apparaîtront à sa place!

mv `ls` /foo      # Exact same badness as the ''for'' thing.
74
lhunath

Vous ne savez pas exactement ce que vous essayez de réaliser ici, mais voici une possibilité:

La partie "xargs" est la pièce importante, tout le reste est juste configuré. Cela a pour effet de prendre tout ce qui sort "ls" et d'y ajouter une extension ".txt".

 $ mkdir xxx # 
 $ cd xxx 
 $ toucher a b c x y z 
 $ ls 
 a b c x y z 
 $ ls | xargs -Ifile fichier mv file.txt 
 $ ls 
 a.txt b.txt c.txt x.txt y.txt z.txt 
 $ 
 $

Quelque chose comme cela pourrait également être réalisé par:

 $ touchez a b c x y z 
 $ pour i dans `ls`; do mv $ i $ {i} .txt; fait 
 $ ls 
 a.txt b.txt c.txt x.txt y.txt z.txt 
 $ 
 $

J'aime mieux la deuxième façon. Je ne me souviens JAMAIS comment xargs fonctionne sans lire la page de manuel ou aller dans mon fichier "cute tricks".

J'espère que cela t'aides.

5
Craig Wright

Check-out find -exec {} car cela pourrait être une meilleure option que ls mais cela dépend de ce que vous essayez de réaliser.

5
sybreon

Jusqu'à présent, aucune des réponses n'est sûre pour les noms de fichiers comportant des espaces. Essaye ça:

for i in *; do mv "$i" some_dir/; done

Vous pouvez bien sûr utiliser n'importe quel motif global à la place de *.

4
Curt J. Sampson

Vous ne devez pas utiliser la sortie de ls comme entrée d'une autre commande. Les fichiers avec des espaces dans leurs noms sont difficiles, tout comme l'inclusion de séquences d'échappement ANSI si vous avez:

alias ls-'ls --color=always'

par exemple.

Utilisez toujours find ou xargs (avec -0) ou globbing.

De plus, vous n'avez pas dit si vous souhaitez déplacer des fichiers ou les renommer. Chacun serait traité différemment.

edit: ajouté -0 à xargs (merci pour le rappel)

1

Les backticks fonctionnent bien, comme d'autres l'ont suggéré. Voir aussi xargs. Et pour des choses vraiment compliquées, canalisez-le dans sed, faites la liste des commandes que vous voulez, puis exécutez-le à nouveau avec la sortie de sed canalisée dans sh.

Voici un exemple avec find, mais cela fonctionne aussi très bien avec ls:

http://github.com/DonBranson/scripts/blob/f09d24629ab6eb3ce509d4d3078818430306b063/jarfinder.sh

0
Don Branson
/bin/ls | tr '\n' '\0' | xargs -0 -i% mv % /path/to/destdir/

"Utilisation inutile de ls", mais devrait fonctionner. En spécifiant le chemin complet vers ls (1), vous évitez les conflits avec l'aliasing de ls (1) mentionné dans certains des articles précédents. La commande tr (1) avec "xargs -0" fait fonctionner la commande avec des noms de fichiers contenant (ugh) des espaces. Cela ne fonctionnera pas avec les noms de fichiers contenant des sauts de ligne, mais avoir des noms de fichiers comme celui-ci dans le système de fichiers est de poser des problèmes, donc ce ne sera probablement pas un gros problème. Mais des noms de fichiers avec des nouvelles lignes pourraient exister, donc une meilleure solution serait d'utiliser "find -print0":

find /path/to/srcdir -type f -print0 | xargs -0 -i% mv % dest/
0
sunny256