Comment gérer les espaces dans une variable

Je travaille sur des scripts:

for x in `find ./ -name *.pdf` do echo pathname $x done 

Mes noms de files sont Test1 ( Volume II) , Test2 ( Volume II) . Je reçois un return de

 pathname Test1 pathname ( pathname Volume pathname II … 

Comment l'get pour restr comme une variable?

    Comme dit plusieurs fois sur ce site, en laissant une extension de variable (comme dans $var ) ou une substitution de command (comme dans `cmd` ou $(cmd) ) (ou arithmetic expansion la plupart des shells) non cotée dans les shells Bourne / POSIX est l'opérateur split + glob.

    Le contenu de $var ou la sortie de cmd (sans les caractères de return arrière) est divisé en fonction de la valeur actuelle de la variable spéciale $IFS qui contient par défaut les caractères SPC, TAB et NL (et NUL dans zsh ) et chaque mot résultant de cette division est sujet à la génération de nom de file, également appelé " globbing" .

    Par exemple, si vous find ./foo bar.pdf\n./*foo*\tbar.pdf\n ( \t signifiant TAB et \n NL), avec la valeur par défaut de $IFS , ./foo bar.pdf\n./*foo*\tbar.pdf (nouvelle ligne supprimée), puis divisée en ./foo , bar.pdf , ./*foo* et foo.pdf et ./*foo* qui est un motif générique sera étendu en autant d'arguments qu'il y a de files non cachés dans le directory courant dont le nom contient foo .

    Si vous souhaitez fractionner uniquement les caractères de nouvelle ligne, vous devez définir $IFS sur newline uniquement:

     IFS=' ' 

    Si vous ne souhaitez pas que les templates generics soient développés, vous devez le désactiver avec

     set -f 

    Cependant, notez que newline est aussi valide qu'un caractère dans un nom de file, donc plus généralement, la sortie find -print ne peut pas être post-traitée de manière fiable.

    Une sortie comme:

     ./a.pdf ./b.pdf 

    soit les files a.pdf et b.pdf du directory courant, soit le file appelé b.pdf dans le a.pdf\n. annuaire.

    Certaines implémentations comme GNU find (d'où il provient) ont un prédicat -print0 pour sortir le nom du file suivi d'un caractère NUL au lieu d'un caractère NL. Avec la find standard, vous pouvez utiliser -exec printf '%s\0' {} + avec le même résultat. NUL est le seul caractère qui ne peut pas apparaître dans un nom de file.

    Cependant, zsh est le seul shell qui peut stocker un caractère NUL dans ses variables (comme le caractère $IFS ), donc:

     IFS=$'\0' for i in `find ... -print0`; do ... done 

    (pas besoin de set -f dans zsh puisque zsh ne fait pas de globulation lors de la substitution de la command) fonctionnera dans zsh mais pas dans d'autres shells.

    Le mieux, et de manière portative, est d'avoir find appel les commands que vous voulez exécuter sur ces files. Comme @Gnouc suggère:

     find . -name '*.pdf' -exec the command {} \; 

    Si vous avez besoin de quelque chose de plus complexe impliquant des instructions shell, vous pouvez toujours faire des choses comme:

     find . -name '*.pdf' -exec sh -c ' for i do something complex with "$i" done' sh {} + 

    Avec zsh ou bash , vous pouvez aussi faire:

     find . -name '*.pdf' -print0 | while IFS= read -r -d '' file; do whatever with "$file" done 

    Notez cependant que le stdin dans la boucle est affecté.

    zsh (depuis 1990) a la plupart des fonctionnalités de find incluses dans ses capacités de globing grâce à une syntaxe où vous pouvez spécifier n'importe quel niveau de sous-directorys ( (*/)# syntaxe ou sa forme plus simple **/ ) et les qualificatifs de globing pendentif du -type f , -mtime , -perm … en find ).

    Le **/ partie de cela a été copié par ksh93 en 2003, fish en 2005, bash en 2009 et tcsh en 2010 (même si tcsh également copié le ***/ part). Et tous ne l'activent pas par défaut. Malheureusement, notez que bash et fish ** suivent des liens symboliques vers des directorys (comme -L / -follow dans find , ou *** dans zsh ou tcsh ).

    Dans ces shells, vous pouvez find des files pdf dans n'importe quel niveau de sous-directorys sans avoir à countr sur find , mais notez cette mise en garde ci-dessus sur les fish et bash , et seulement zsh a les qualificatifs globbing.

    Ainsi, par exemple, l'équivalent zsh de:

     find . -name '*.pdf' -type f -exec ls -ld {} + 

    serait:

     ls -ld ./**/*.pdf(D.) 

    Avec bash , vous devrez faire quelque chose comme:

     shopt -s failglob shopt -s globstart files=(./**/*.pdf) && for i do [ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@" shift done && ls -ld "$@" 

    Le moyen le plus sûr est d'utiliser la fonction globulation:

     for file in *pdf; do echo pathname "$file"; done 

    Si vous avez besoin de find tous les pdf récursivement, faites ceci:

     shopt -s globstar for file in **/*pdf; do echo pathname "$file"; done 

    Il y a quelques options. En collant avec une solution de bouclage, vous pouvez utiliser read pour lire une ligne à la fois dans une variable:

     find . -name "*.pdf" | while IFS= read -rx do echo pathname "$x" done 

    Vous pouvez également utiliser xargs -0 et find -print0 pour gérer n'importe quel type de caractères spéciaux:

     find . -name "*.pdf" -print0 | xargs -0 -L1 echo pathname 

    Essayez ceci en une seule ligne:

     find ./ -name "*.pdf" -exec echo pathname {} \; 

    Après avoir cherché quelques heures pour find une solution pour le shell par défaut de FreeBSD (/ bin / sh), j'ai finalement trouvé ma propre solution. Cela implique d'utiliser sed pour détruire / créer des espaces. Cela vous permettra également de changer les variables de votre boucle et de les utiliser de manière externe, contrairement à l'utilisation d'une boucle while (while while spawn subshells in / bin / sh).

     REMOTE_FOLDER=/media/images FILE_LIST=$(find $REMOTE_FOLDER | sed 's/ /\/\/\/\//g') for FILE in $FILE_LIST; do FILE=$(echo $FILE | sed 's/\/\/\/\// /g') # do whatever you need to do with $FILE done