Je veux diriger les noms de fichiers vers d'autres programmes, mais ils s'étouffent tous lorsque les noms contiennent des espaces.
Disons que j'ai un fichier appelé.
foo bar
Comment puis-je obtenir find
le nom correct?
Evidemment je veux:
foo\ bar
ou:
"foo bar"
EDIT : Je ne veux pas passer par xargs
, je veux obtenir une chaîne correctement formatée find
afin que je puisse diriger la chaîne de noms de fichiers directement vers un autre programme.
-exec
drapeau avecfind
? vous pourriez potentiellement atténuer cette erreur et rendre votre commande plus efficace en la faisant-exec
au lieu de la rediriger vers d'autres commandes. Just my $ .02find
formate très bien les noms de fichiers; ils sont écrits un nom par ligne. (Bien sûr, cela est ambigu si un nom de fichier contient un caractère de nouvelle ligne.) Le problème est donc que l'extrémité de réception "s'étouffe" lorsqu'elle obtient un espace, ce qui signifie que vous devez nous dire quelle est l'extrémité de réception si vous voulez une réponse significative. .find
de proposer (par exemple) une option pour sortir les noms de fichiers dans un format adapté au shell. En général, cependant, l' extension-print0
GNUfind
fonctionne bien pour de nombreux autres scénarios (aussi), et vous devez apprendre à l'utiliser dans tous les cas.ls $(command...)
ne fait pas passer la listestdin
. Il place la sortie de$(command...)
directement dans la ligne de commande. Dans ce cas, c'est le shell qui lit à partir du c, et il utilisera la valeur actuelle de$IFS
pour décider comment séparer les mots en sortie. En général, il vaut mieux utiliserxargs
. Vous ne remarquerez pas un coup de performance.find -printf '"%p"\n'
ajoutera des guillemets doubles autour de chaque nom trouvé, mais ne citera pas correctement les guillemets doubles dans un nom de fichier. Si vos noms de fichiers n'ont pas de guillemets doubles incorporés, vous pouvez ignorer le problème: ou passer parsed 's/"/&&/g;s/^""/"/;s/""$/"/'
. Si vos noms de fichiers finissent par être traités par le shell, vous devriez probablement utiliser des guillemets simples au lieu de guillemets doubles (sinonsweet$HOME
cela deviendra quelque chose commesheet/home/you
). Et ce n'est toujours pas très robuste contre les noms de fichiers avec des retours à la ligne. Comment voulez-vous gérer cela?Réponses:
POSIX:
Avec
find
supports-print0
etxargs
supports-0
:-0
L'option indique à xargs d'utiliser le caractère ASCII NUL au lieu de l'espace pour terminer (séparer) les noms de fichiers.Exemple:
la source
ls $(find . -maxdepth 1 -type f -print0 | xargs -0)
je reçoisls: cannot access ./foo: No such file or directory
ls: cannot access bar: No such file or directory
$(..)
"$(..)"
find
etxargs
.L'utilisation
-print0
est une option, mais tous les programmes ne prennent pas en charge l'utilisation de flux de données délimités par des octets, vous devrez donc utiliserxargs
avec le-0
option pour certaines choses, comme l'a noté la réponse de Gnouc.Une alternative serait d'utiliser
find
les options-exec
ou-execdir
. Le premier des éléments suivants alimentera les noms de fichierssomecommand
un par un, tandis que le second s'étendra à une liste de fichiers:Vous trouverez peut-être qu'il vaut mieux utiliser la globalisation dans de nombreux cas. Si vous avez un shell moderne (bash 4+, zsh, ksh), vous pouvez obtenir un globbing récursif avec
globstar
(**
). En bash, vous devez définir ceci:J'ai une ligne disant
shopt -s globstar extglob
dans mon .bashrc, donc c'est toujours activé pour moi (et les globs étendus aussi, qui sont également utiles).Si vous ne voulez pas de récursivité, utilisez simplement à la
./*.txt
place, pour utiliser chaque * .txt dans le répertoire de travail.find
a des capacités de recherche fine très utiles et est obligatoire pour des dizaines de milliers de fichiers (auquel cas vous rencontrerez le nombre maximal d'arguments du shell), mais pour une utilisation quotidienne, il est souvent inutile.la source
Personnellement, j'utiliserais l'
-exec
action de recherche pour résoudre ce genre de problème. Ou, si nécessaire,xargs
ce qui permet une exécution parallèle.Cependant, il existe un moyen d'obtenir
find
une liste de noms de fichiers lisibles par bash. Sans surprise, il utilise-exec
etbash
notamment une extension de laprintf
commande:Cependant, bien que cela imprime correctement les mots échappés du shell, il ne sera pas utilisable avec
$(...)
, car$(...)
n'interprète pas les guillemets ou les échappements. (Le résultat de$(...)
est sujet au fractionnement de mots et à l'expansion des noms de chemin, sauf s'il est entouré de guillemets.) Donc, ce qui suit ne fera pas ce que vous voulez:Ce que vous auriez à faire, c'est:
(Notez que je n'ai fait aucune tentative réelle pour tester la monstruosité ci-dessus.)
Mais alors vous pourriez aussi bien faire:
la source
ls
scénario capture correctement le cas d'utilisation de l'OP, mais ce n'est que de la spéculation, car on ne nous a pas montré ce qu'il essayait réellement d'accomplir. Cette solution fonctionne en fait très bien; J'obtiens la sortie que j'attendais (vaguement) pour tous les noms de fichiers amusants que j'ai essayés, y compristouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
eval
est que vous n'avez pas encore à la passereval
; vous pouvez l'enregistrer dans un paramètre et l'utiliser plus tard, peut-être plusieurs fois avec différentes commandes. Cependant, OP ne donne aucune indication que c'est le cas d'utilisation (et si c'était le cas, il serait peut-être préférable de mettre les noms de fichiers dans un tableau, bien que ce soit aussi délicat.)vous obtiendrez les fichiers et répertoires contient des espaces
vous obtiendrez les fichiers contient des espaces
vous obtiendrez les répertoires contient des espaces
la source
la source