Copier la liste des fichiers

35

J'ai une liste de fichiers séparés par des espaces dans un fichier list.txt. Je voudrais 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 demandé autant :)

Après avoir essayé la solution ci-dessus une deuxième fois, il semble que cpl'aide à l'utilisation ait été imprimée car le dossier new_foldern'existait pas encore! Désolé, cela semble être une erreur stupide ... Quand je crée le dossier avant, ça marche.

Cependant, j'ai beaucoup appris de toutes vos réponses et solutions, merci pour le temps que vous prenez pour les écrire.

Klaus
la source
Permettez-moi de clarifier une chose: la liste contient tous les noms de fichiers sur une seule ligne, séparés uniquement par un espace? Ou les noms sont-ils chacun sur une ligne distincte?
Télémaque
Le tout dans une chaux, séparée par un espace. La solution de Doug a fonctionné :) Merci pour la réponse.
Klaus
C'est drôle ... J'ai testé ma solution avec une ligne dans laquelle chaque nom était sur une ligne distincte.
Doug Harris
"ça n'a pas marché" n'a pas beaucoup de sens. Exactement comment cela n'a pas fonctionné. Ça fonctionne bien pour moi.
pause jusqu'à nouvel ordre.
Désolé: il a montré l'aide à l'utilisation de cp. Voir ma mise à jour ci-dessus.
Klaus

Réponses:

34

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 des versions GNU / Linux. Ce xargsqui vient de GNU findutils n'a pas -Jmais il a -Iqui est similaire (comme l'a souligné Dennis Williamson dans un commentaire). La version OS X de xargs a les deux -Iet -Jqui ont des comportements légèrement différents - l'un ou l'autre fonctionnera pour cette question d'origine.

$ 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
Doug Harris
la source
3
Mon xargsne pas -Jqu'il a ce -Iqui semble faire la même chose. De quelle version xargset de quel OS / distribution disposez-vous?
pause jusqu'à nouvel ordre.
J'ai testé cela sur Mac OS X 10.6 (alias snow leopard), il s'agit donc de la version de xargs livrée avec le système d'exploitation. L'exécution xargs --versionrenvoie un message d'erreur d'utilisation. Il s'agit probablement d'une version BSD de xargs. L'utilisation de -Iet -Jest subtilement différente. Je mettrai à jour ma réponse avec des exemples bien formatés.
Doug Harris
Oui, je travaille avec Mac OSX 10.6. Merci pour les précisions sur -Jet -I:)
Klaus
C'est une bonne astuce pour utiliser echo pour vérifier vos commandes avant de les exécuter.
Liam
1
En utilisant la même commande ci-dessus avec les modifications nécessaires uniquement, comment copier les fichiers avec la création de leurs répertoires et sous-répertoires dans lesquels ils se trouvent, séparés par une barre oblique?
Vicky Dev
54

Pas besoin du cattout:

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 shell. Notez que certaines variables ne sont pas entre guillemets ou que les forboucles sont utilisées spécifiquement parce que l'OP a spécifié que les noms de fichiers sont séparé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 fichier contiennent des espaces blancs.

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
En pause jusqu'à nouvel ordre.
la source
noob question: pourquoi est-il -tnécessaire xargs -a list.txt cp -t new_folder?
Yibo Yang
1
@YiboYang: -tc'est une option decp . Il copie tous les arguments source dans le répertoire cible spécifié. Ici, il permet de spécifier la cible avant la source qui est ajoutée par la xargscommande.
pause jusqu'à nouvel ordre.
Ceci est utile mais que se passe-t-il si list.txt contient un chemin de fichiers et de dossiers mixtes? Dans ce cas, la commande ne fonctionne pas correctement. Il signale "omettant le répertoire ..." et si -R est spécifié à la commande cp, l'erreur "n'est pas un répertoire" est signalée dès que le premier fichier de la liste est rencontré.
Bemipefe
7

J'ai eu une réponse gênante à propos de ce sujet plus tôt, 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 d'origine. Mais puisque personne n'a dit cette chose très basique, je pense que cela vaut la peine de la mettre ici.

La question d'origine 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 en quelque sorte extraire les éléments du fichier d'une manière spécifique. Cependant, lorsque le shell traite une ligne de commande, la première chose qu'il fait est de séparer la liste des arguments en jetons et (voici le bit que personne n'a dit carrément) des espaces séparent les jetons . (Les nouvelles lignes séparent également les jetons, c'est pourquoi le test de Doug Harris avec une liste séparée par les nouvelles lignes a eu le même résultat.) Autrement dit, le shell attend et peut déjà gérer une liste séparée par des espaces.

Donc, tout ce que vous devez faire ici est de mettre la liste séparée par des espaces (que vous avez déjà) au bon endroit dans votre commande. Votre commande est une variation à ce sujet:

cp file1 file2 file3...file# target

Le seul inconvénient est que vous souhaitez obtenir la liste des fichiers 1 à # à partir de votre fichier texte.

Comme Dennis le souligne dans son commentaire, votre tentative initiale ( cpcat list.txt new_folder) aurait déjà dû fonctionner. Pourquoi? Parce que la commande interne cat list.txtest d'abord traitée par le shell et se développe en file1 file2 file3...file#, ce qui est exactement ce que le shell attend et veut à cette partie de la commande. Si cela ne fonctionnait pas, alors (1) vous aviez une faute de frappe ou (2) vos noms de fichiers étaient étranges (ils avaient 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 de fichiers nécessaires cppour travailler, en plaçant cette liste à sa place dans la commande entière. Encore une fois, la commande elle-même est structurée:

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

$()oblige le shell à exécuter la commande entre parenthèses, puis à substituer sa sortie à ce point de la ligne la plus grande. Ensuite, le shell exécute la ligne dans son ensemble. Soit dit en passant, $()est une version plus moderne de ce que vous faisiez déjà avec des backticks (`). Suivant: <est un opérateur de redirection de fichiers. Il indique au shell de vider le contenu de list.txtl'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

De toute évidence, l'étape 2 est simplement la cpcommande normale que vous vouliez.

Je me rends compte que je bat beaucoup ce cheval (peut-être très mort), mais je pense que ça vaut le coup. Comprendre exactement comment le shell traite une commande peut vous aider à mieux l'écrire et à simplifier beaucoup. Il vous montrera également où les problèmes sont susceptibles de se cacher. Dans ce cas, par exemple, ma première question devrait vous concerner des noms de fichiers amusants ou une possible faute de frappe. Aucune acrobatie n'était nécessaire.

Télémaque
la source
@Klaus Vous êtes les bienvenus. Pour être franc, j'étais surtout ennuyé par moi-même après avoir lu la réponse de Dennis et réalisé que j'avais complètement raté la partie clé du problème. La solution que vous aimez vient également de là.
Télémaque
C'est une très vieille réponse, mais si vous devez recréer la structure des dossiers, vous avez besoin du --parentsdrapeau. Vous pouvez le rendre verbeux avec -vpour un enregistrement de ce qui a été copié. Si vous souhaitez conserver les autorisations, vous avez besoin du drapeau -aou -p. -À- dire, cp --parents -av $(<list.txt) new_folder. Tout cela en supposant que votre liste de fichiers est vraiment tous les fichiers, sinon vous pouvez également avoir besoin de l' -rindicateur pour recurse.
dhaupin
@dhaupin En fait, l'OP déclare qu'il était sous macOS. Donc pour ce que ça vaut, le --parentsdrapeau n'existe pas là-bas. Leur version (dérivée de BSD) cpne fournit que des options courtes, pas des options longues de style GNU. Pour un style BSD cp, l'indicateur de copie récursive est en majuscules -Ret non en minuscules.
Télémaque