web-dev-qa-db-fra.com

Copier la liste des fichiers

J'ai une liste de fichiers séparés par des espaces dans un fichier list.txt. J'aimerais les copier dans un nouveau dossier.

J'ai essayé de faire:

cp `cat list.txt` new_folder

mais cela n'a pas fonctionné.

Comment ferais-tu ceci ?

Mise à jour:

Merci pour toutes vos réponses très intéressantes. Je n'ai pas trop demandé :)

Après avoir essayé la solution ci-dessus une seconde fois, il semble que cp a imprimé l'aide à l'utilisation, car le dossier new_folder n'existait pas encore! Désolé, cela semble être une erreur stupide ... Lorsque je crée le dossier auparavant, cela fonctionne.

Cependant, j'ai beaucoup appris de vos réponses et solutions, merci du temps que vous avez pris pour les écrire.

31
Klaus

Essayez d'utiliser xargs:

cat list.txt | xargs -J % cp % new_folder

Mise à jour:

Je l’ai fait sur OS X, qui a une version différente de celle de GNU/Linux. La xargs qui provient de GNU findutils n’a pas -J mais elle a -I qui est similaire (comme l’a souligné Dennis Williamson dans un commentaire). La version OS x de xargs comporte à la fois -I et -J qui ont des comportements légèrement différents - soit cela fonctionnera pour cette question initiale.

$ cat list.txt
one
two.txt
three.rtf

$ cat list.txt | xargs -J % echo cp % new_folder
cp one two.txt three.rtf new_folder

$ cat list.txt | xargs -I % echo cp % new_folder
cp one new_folder
cp two.txt new_folder
cp three.rtf new_folder
33
Doug Harris

Pas besoin de cat du tout:

xargs -a list.txt cp -t new_folder

Ou en utilisant des options longues:

xargs --arg-file=list.txt cp --target-directory=new_folder

Voici quelques versions de Shell. Notez que certaines variables sont des boucles non mises entre guillemets ou for sont utilisées spécifiquement parce que l'OP a spécifié que les noms de fichiers sont délimités par des espaces. Certaines de ces techniques ne fonctionneront pas pour une entrée de nom de fichier par ligne dans laquelle les noms de fichiers contiennent des espaces.

Frapper:

for file in $(<list.txt); do cp "$file" new_folder; done

ou

cp $(<list.txt) new_folder

sh (ou Bash):

# still no cat!
while read -r line; do for file in $line; do cp "$file" new_folder; done; done < list.txt

ou

# none here either
while read -r line; do cp $line new_folder; done < list.txt

ou

# meow
for file in $(cat list.txt); do cp "$file" new_folder; done

ou

# meow
cp $(cat list.txt) new_folder
49
Dennis Williamson

J'avais eu une réponse embarrassante tout à l'heure ici, mais la réponse de Denis m'a rappelé que j'avais raté la chose la plus élémentaire. J'ai donc supprimé ma réponse originale. Mais puisque personne n'a dit cette chose très fondamentale, je pense que ça vaut la peine de la mettre ici.

La question initiale est "J'ai un fichier texte avec une liste de noms de fichiers séparés par des espaces. Comment puis-je les copier dans un répertoire cible." Au début, cela peut sembler délicat ou compliqué, car vous pensez que vous devez extraire d'une manière ou d'une autre les éléments du fichier d'une manière spécifique. Cependant, lorsque le shell traite une ligne de commande, la première chose à faire est de séparer la liste d'arguments en jetons et (voici le bit que personne n'a dit carrément) les espaces séparent les jetons . (Les nouvelles lignes séparent également les jetons, raison pour laquelle le test de Doug Harris avec une liste séparée par une nouvelle ligne a le même résultat.) En d'autres termes, le Shell attend et peut déjà gérer une liste séparée par des espaces.

Il vous suffit donc de placer la liste (que vous avez déjà) séparée par des espaces à la bonne place dans votre commande. Votre commande est une variation sur ceci:

cp file1 file2 file3...file# target

Le seul problème est que vous souhaitez obtenir la liste des fichiers 1 à # de votre fichier texte.

Comme Dennis le souligne dans son commentaire, votre tentative initiale (cpcat list.txtnew_folder) aurait déjà dû fonctionner. Pourquoi? Parce que la commande interne cat list.txt est d'abord traitée par le shell et se développe en file1 file2 file3...file#, ce qui correspond exactement aux attentes et aux attentes du shell à cette partie de la commande. Si cela ne fonctionne pas, alors (1) vous avez une faute de frappe ou (2) vos noms de fichiers sont étranges (ils ont des espaces ou d’autres caractères inhabituels).

La raison pour laquelle toutes les réponses de Dennis fonctionnent est simplement qu'elles fournissent la liste nécessaire des fichiers sur lesquels cp doit travailler, en plaçant cette liste là où elle se trouve dans la commande entière. Encore une fois, la commande elle-même a la structure suivante:

cp list-of-files target_directory

Il est facile de voir comment tout cela se réunit dans cette version:

cp $(<list.txt) new_folder

$() force le shell à exécuter la commande entre parenthèses, puis à substituer sa sortie à ce point dans la ligne la plus grande. Ensuite, le shell exécute la ligne dans son ensemble. Au fait, $() est une version plus moderne de ce que vous faisiez déjà avec des backticks (`). Next: < est un opérateur de redirection de fichier. Il demande au shell de vider le contenu de list.txt en entrée standard. Puisque le bit $() est traité en premier, voici ce qui se passe par étapes:

  1. cp $(<list.txt) new_folder# split line into three tokens: cp, $(<list.txt), new_folder
  2. cp file1 file2 file3...file# new_folder# substitute result of $(<list.txt) into the larger command

Évidemment, l'étape 2 est simplement la commande cp normale que vous vouliez.

Je me rends compte que je bat beaucoup ce cheval (peut-être très mort), mais je pense que cela en vaut la peine. Comprendre exactement comment le shell traite une commande peut vous aider à mieux l'écrire et à le simplifier beaucoup. Cela vous indiquera également où les problèmes risquent de se cacher. Dans ce cas, par exemple, ma première question aurait dû porter sur des noms de fichiers amusants ou sur une possible faute de frappe. Aucune acrobatie n'était nécessaire.

7
Telemachus