Je veux faire quelque chose à plusieurs reprises sur une liste de fichiers. Les fichiers des questions ont des espaces dans leurs noms:
david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha
-rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy)
Maintenant, je veux statuer chacun de ces fichiers:
david@david: echo '
for file in $(ls)
do
stat $file
done' | bash
(J'utilise echo et un pipe pour écrire des commandes multilignes.)
Lorsque je fais cela, cela fonctionne correctement sur les fichiers qui n'ont pas d'espace dans leurs noms. Mais les autres ...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
Le passage $(ls)
à "$(ls)"
ou $file
à "$file"
ne fonctionne pas. Que puis-je faire?
Éditer:
echo '
for files in *
do
stat "$files"
done' | bash
fait l'affaire! Comme je suis nouveau dans bash, je veux garder les choses aussi simples que possible - donc rien avec essayer d'échapper aux espaces, ou d'utiliser xargs
ou la solution avec read -r
, bien qu'ils résolvent le problème.
Comme certains l'ont demandé: Oui, utiliser cela au lieu de stat *
est bizarre. Mais je voulais juste trouver un moyen général d'appliquer la même commande sur un tas de noms de fichiers en bash, en utilisant une boucle for. Ainsi stat
pourrait représenter gzip
, gpg
ou rm
.
la source
stat *
? (;-)Réponses:
La citation multiple de la
echo '
complique la chose.Vous pouvez simplement utiliser:
Mais aussi
... et si vous voulez collecter les fichiers et ensuite appliquer la commande (pourquoi?) vous pouvez aller avec (mais attention aux fichiers contenant de nouvelles lignes ... (1))
... et si vous voulez aussi des fichiers cachés, utilisez-les simplement
* .*
comme modèle, mais n'oubliez pas cela.
et..
ils seront dans le jeu .En passant, vous ne devriez pas analyser la
ls
sortie .(1) mais si vous avez des noms de fichiers avec des retours à la ligne, vous le méritez un peu ... ;-)
la source
for f in *; do command "$f"; done
. Ne jamais analyserls
, certainement jamais le faire enfor
boucle et pourquoi l'utiliserecho
?do
,|
,&&
etc, vous pouvez continuer sur la nouvelle ligne. Soit cela, soit utilisez heredocs. Aucune raison d'utiliserecho
et cela peut aussi causer des problèmes.Sur une note latérale: vous pouvez diviser les commandes longues / compliquées sur plusieurs lignes en ajoutant un espace suivi d'une barre oblique inverse et en frappant Enterchaque fois que vous voulez commencer à écrire dans une nouvelle ligne, au lieu de bifurquer plusieurs processus en utilisant
echo [...] | bash
; vous devez également les mettre entre$file
guillemets, pour éviterstat
de casser en cas de noms de fichiers contenant des espaces:Le problème est que cela se
$(ls)
développe en une liste de noms de fichiers contenant des espaces, et la même chose se produira également avec"$(ls)"
.Même en résolvant ce problème, cette méthode se cassera toujours sur les noms de fichiers contenant des barres obliques inverses et sur les noms de fichiers contenant des sauts de ligne (comme souligné par terdon).
Une solution pour les deux problèmes serait de diriger la sortie de
find
vers unewhile
boucle en cours d'exécution deread -r
sorte qu'à chaque itération,read -r
elle stocke une ligne defind
sortie dans$file
:la source
ls
. Déjà.ls
. Il existe des moyens meilleurs et plus robustes. Vous pourriez également vouloir lire ceci: Pourquoi * pas * analyser `ls`? pour plus de détails.ls
, c'estfor
-for
itère sur les mots (séparés par IFS) donnés après lein
mot clé`for
etls
.for f in *
serait bien, par exemple.Utilisez le bon vieux
find
, fonctionne avec des fichiers cachés, des nouvelles lignes et des espaces.ou tout autre au lieu de
stat
la source
En tant que gars R, j'ai déjà trouvé une solution de contournement dans R:
Je sais, c'est fou. Je souhaite que la sortie de
ls
soit plus facile à analyser ... R peut gérer les espaces, car dir () renvoie une valeur de caractère entre guillemets. Tout ce qui se trouve entre les guillemets est alors un nom de fichier valide avec des espaces.la source
for f in *
, appuyez sur Entrée et continuez sur une nouvelle lignedo
:, appuyez de nouveau surstat "$f"
Entrée,, Entrez de nouveau,done
entrez. C'est une commande bien répartie sur 4 lignes et ne se cassera pas sur tout type de nom de fichier.J'ai rencontré d'autres instances de problèmes d'espaces dans les boucles for, donc la commande suivante (imo plus robuste) est ce que j'utilise généralement. Il s'intègre également bien dans les tuyaux.
Vous pouvez combiner cela avec
grep
ou utiliser à lafind
place:la source
-E
option àgrep
, sans laquelle elle ne sera pas reconnue+
dans une expression régulière. Même alors, c'est une balançoire et un échec sur cette question, car votregrep
supprime les noms de fichiers contenant des espaces . Il supprime également les noms de fichiers contenant des chiffres (chiffres) et la ponctuation (par exemple, les parenthèses), comme le font les exemples de la question. Et cela ne mentionne même pas les noms de fichiers qui contiennent une nouvelle ligne ou commencent par-
(tiret).Cette réponse résoudra le problème de l'analyse
ls
et prendra soin des backspaces et des nouvelles lignesEssayez ceci, cela résoudra votre problème en utilisant IFS séparateur de champ interne.
Mais vous pouvez également le résoudre facilement sans avoir besoin d'analyser la sortie ls en utilisant
la source
IFS
ici: la citation de la variable devrait être suffisante pour empêcher le fractionnement de mots, sûrement?ls
.Au lieu de cela, vous pouvez renommer vos fichiers en remplaçant l'espace par un autre caractère tel que le trait de soulignement, afin de vous débarrasser de ce problème:
Pour ce faire, exécutez facilement la commande:
la source