Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 01/10/2008, à 17:01

®om

Séparateur des résultats find

Salut,

Un cas typique d'utilisation d'un script est d'effectuer une action pour chaque fichier trouvé grâce à find, par exemple grâce au paramètre -exec :

find -name '*.ogg' -exec echo {} \;

Mais quand on veut faire des actions plus complexes, vu qu'on ne peut pas faire :

find -name '*.ogg' -exec echo {} && uneautreaction || autrechose \;

on voudrait faire quelque chose comme :

find -name '*.ogg' | while read -r line
do
    echo "$line"
    do_something "$line"
end

Le problème, c'est que dans ce cas, si un nom de fichier contient un retour à la ligne (ce qui est rare, je vous l'accorde), ça ne fonctionne pas.

On a alors l'option -print0 :

find -name '*.ogg' -print0 | ...

mais que peut-on en faire derrière pour parcourir tous les fichiers et effectuer des actions?

Une contrainte importante est que ça fonctionne à la fois sur bash et sur sh (dash).

Dernière modification par ®om (Le 01/10/2008, à 17:01)

Hors ligne

#2 Le 01/10/2008, à 17:48

Link31

Re : Séparateur des résultats find

®om a écrit :

Mais quand on veut faire des actions plus complexes, vu qu'on ne peut pas faire :

find -name '*.ogg' -exec echo {} && uneautreaction || autrechose \;
find -name '*.ogg' -exec sh -c 'echo {} && uneautreaction || autrechose' \;

Hors ligne

#3 Le 01/10/2008, à 17:52

®om

Re : Séparateur des résultats find

Ah ouais pas mal.

Donc pour faire un long code, lisible, utilisant les résultats du find :

find -name '*.ogg' -exec sh -c'
mon {}
long {}
code' \;

Ça ne me semble pas très propre, si?

Autre problème, si on modifie #!/bin/sh en #!/bin/bash au début, et qu'on veut utiliser du bash un peu partout, il faudra remplacer sh par bash ici aussi...

EDIT: apparemment, il vaut mieux faire :

find -name '*.ogg' -exec sh -c'
mon "$1"
long "$1"
code' - {} \;

Dernière modification par ®om (Le 01/10/2008, à 18:15)

Hors ligne

#4 Le 01/10/2008, à 20:28

Alain.g

Re : Séparateur des résultats find

®om a écrit :

On a alors l'option -print0 :
mais que peut-on en faire derrière pour parcourir tous les fichiers et effectuer des actions?

Une contrainte importante est que ça fonctionne à la fois sur bash et sur sh (dash).

Puis-je demander pourquoi c'est une contrainte importante ?
Je donne tout de même la solution en bash, même si ça ne fonctionne pas en dash...
Cela consiste à utiliser l'option -d (délimiteur) de read avec la valeur \0 mis entre $' ' pour qu'il soit bien interprété (en dash, read n'a que les options -r et -p...)

find * -print0 | while IFS= read -rd $'\0' i
do
	command "$i"
done

IFS= entre while et read est utile s'il y a des espaces en début ou en fin de chaîne (pas courant dans les noms de fichier mais plus fréquent/probable que des sauts de ligne. Ça fonctionne en dash également)


Xubuntu Karmic !

Hors ligne

#5 Le 02/10/2008, à 09:05

®om

Re : Séparateur des résultats find

Merci de vos réponses.

Dans ta solution Alain.g, pourquoi avoir à la fois IFS= et -d $'\0' ?
Ça me semble redondant, non?

Sinon, j'avais trouvé une solution qui me convenait avec -exec, mais maintenant, dans le cas où l'on veut utiliser un grep derrière :

find -name '*.ogg' -print0 | grep '....' -z

Comment itérer sur les résultats?

(à part avec while IFS= read -rd $'\0' i, qui ne fonctionne qu'en bash)

Pour répondre à Alain.g, c'est une contrainte importante dans ma compréhension des scripts shells, pas en pratique smile
Je voudrais essayer de faire des scripts qui supportent #!/bin/sh ET #!/bin/bash, et à défaut, si ce n'est pas possible, connaître d'un côté la solution bash et de l'autre la solution dash.

Au passage, quelle est la différence entre grep -z et grep -zZ?
Ça produit le même résultat, comme si le Z était inutile s'il y avait déjà le z.

Dernière modification par ®om (Le 02/10/2008, à 09:25)

Hors ligne

#6 Le 02/10/2008, à 10:16

Alain.g

Re : Séparateur des résultats find

rom a écrit :

Dans ta solution Alain.g, pourquoi avoir à la fois IFS= et -d \0' ?
Ça me semble redondant, non?

Comme dit, c'est utile pour conserver les espaces en début ou fin.
Dans ce simple exemple, file ne trouve pas le fichier " salut", même en utilisant $'\0'  ; en revanche il le trouve dès qu'on met IFS=""

$ touch " salut"
$ find *ut -print0 | while read -rd $'\0' i ; do file "$i" ; done
salut: ERROR: cannot open `salut' (No such file or directory)
$ find *ut -print0 | while IFS= read -rd $'\0' i ; do file "$i" ; done
 salut: empty
rom a écrit :

crée un fichier a?b

il crée bien un saut de ligne, c'est juste l'affichage de ls en console par défaut qui veut ça :

$ touch "hello
> world"  # ou $'hello\nworld' en bash
$ ls
hello?world
$ ls >/tmp/ls && cat /tmp/ls
hello
world
$ ls --show-control-chars
hello
world
$ echo "$(ls)" 
hello
world
$ echo *
hello
world
rome a écrit :

Je voudrais essayer de faire des scripts qui supportent #!/bin/sh ET #!/bin/bash

/bin/sh et dash ne sont pas la même chose, sauf sous Ubuntu à cause d'un lien symbolique (qui avant était avec bash).

Dernière modification par Alain.g (Le 02/10/2008, à 10:37)


Xubuntu Karmic !

Hors ligne

#7 Le 02/10/2008, à 10:29

®om

Re : Séparateur des résultats find

Merci pour toutes ces précisions smile

Alain.g a écrit :

/bin/sh et dash ne sont pas la même chose, sauf sous Ubuntu à cause d'un lien symbolique (qui avant était avec bash).

Ouais, ce que je veux en fait, c'est qu'un script s'exécute avec tous les shells :

#!/bin/sh
...

quelque soit le shell vers quoi pointe /bin/sh.

Je trouve ça un peu stupide d'autoriser les newline dans les noms de fichiers, ça oblige à doubler la taille des commandes et à se torturer pour vraiment pas grand chose, que personne n'utilisera jamais, mais comme c'est possible...
Ça oblige à se concentrer sur "ce qui n'est pas important" plutôt que sur ce que doit faire le script...

Hors ligne

#8 Le 02/10/2008, à 11:32

BorX

Re : Séparateur des résultats find

xargs n'est-elle pas la commande idéale ?

Extrait de http://www.kalamazoolinux.org/tech/find.html :

xargs

    * Why do we need this "xargs" thing? It's in the presentation title! :-)
      Answer: Speed and efficiency.
          o The second line runs much faster than the first for a large number of files:
                + find / -name core -exec rm -f {} \;
                + rm -f $(find / -name core -print)
            In other words, running "rm" once, with all the filenames on the command line
            is much faster than running "rm" multiple times, once for each file.
          o However, the second line could fail if the number of files is very large and
            exceeds the maximum number of characters allowed in a single command.
          o "xargs" will combine the single line output of find and run commands with multiple
            arguments, multiple times if necessary to avoid the max chars per line limit.
                + find / -name core -print | xargs rm -f
          o The simplest way to see what xargs does, is to run some simple commands:
                + find $HOME -maxdepth 2 -name \*.jpg -exec echo {} \;
                + find $HOME -maxdepth 2 -name \*.jpg | xargs echo

    * Enter the power of ZERO!
          o The 2nd command will fail if any of the files contain a space or other special character:
                + find $HOME -maxdepth 2 -name \*.jpg -exec ls {} \;
                + find $HOME -maxdepth 2 -name \*.jpg | xargs ls
          o Delimiting the file names with NULL fixes the problem:
                + find $HOME -maxdepth 2 -name \*.jpg -print0 | xargs -0 ls

    * Real world example of a very useful set of commands: (This happens to me all the time)
          o Our "webmaster" comes to me and asks if I can "find" all the web pages
            that contain the graphic file "ArmstrongISS.jpg" so they can edit those pages.
                + find /home/httpd \( -name \*.html -o -name \*.php -o -name \*.php3 \) -print0 \
                  | xargs -0 grep -l "ArmstrongISS.jpg"

            Note: add a "-i" parameter to "grep" for a case insensitive search on the string.
          o The above example alone is worth more than double the price of admission! :-)
            Not only does it find files by name, it only displays file names containing a certain string!
            When combining "find" with other Linux commands (like grep) and it's potential use in shell
            scripts, the power is only limited by your imagination! (and your command line skills). :-)
          o Similar examples to demo on my local system:
                + find $HOME \( -name \*txt -o -name \*html \) -print0 | xargs -0 grep -li vpn
                + find $HOME \( -name \*txt -o -name \*html \) -exec grep -li vpn {} +

Dernière modification par BorX (Le 02/10/2008, à 11:34)

Hors ligne