# Obtenir un "ls -R" formaté de manière utilisable : avec find

**URL:** <https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249>\
**Category:** Programmation\
**Created:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249 "2016-02-20T21:19:40Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/1 "2016-02-20T21:19:40Z")

</div>

Salut !

Le formatage de la sortie de la commande

[code]$ ls -R  
.:  
autorun.inf dists install md5sum.txt pool README.mirrors.html README.txt  
css doc live pics README.html README.mirrors.txt syslinux

./css:  
debinstall.css debinstall-print.css

./dists:  
frozen lenny stable testing unstable[/code]

ne me plaît pas du tout. On ne peut pas réutiliser ça avec un script. Ce qu’il faut c’est une sortie du genre :

`./autorun.inf
./css/debinstall.css
./css/debinstall-print.css
./dists/frozen
./dists/lenny/main/binary-i386/...
./dists/lenny/main/debian-installer/...
./dists/lenny/Release
./dists/stable
./dists/testing
./dists/unstable
./md5sum.txt`

Je rame un peu car je ne suis pas habitué aux langages de scripts. J’ai réussi à trouver une solution qui fonctionne à peu près mais qui est absolument horrible :

fichier [lsrec.sh](http://lsrec.sh)

[code]#!/bin/sh

# Le premier argument contient le chemin du repertoire a lister.

# Si le chemin n’est pas termine par un slash, en rajouter un.

echo $1 | sed ‘s%[^/]$%&/%’ \> tmp1  
echo “” \> tmp2

# tant qu’il reste des repertoires a derouler, executer lsrec.awk

while [“`cat tmp1`” != “`cat tmp2`”]; do  
cat tmp1 | awk -f lsrec.awk \> tmp2  
cat tmp2 | awk -f lsrec.awk \> tmp1  
done[/code]

fichier lsrec.awk

[code]#!/usr/bin/awk -f

# si une ligne n’est pas le nom d’un repertoire, ne rien faire

/[^/]$/ {print $0}

# sinon, lister le repertoire en gardant bien tout le chemin grace a sed

//$/ {system(“ls -1p “”$0"” | sed ‘s%…\*%"$0"&%’")}[/code]

Le principe c’est de faire des itérations comme ceci dans un fichier temporaire :

fichier tmp

```auto

```

fichier tmp

`./binary/
./binary.img
./binary.list
./binary.packages`

fichier tmp

`./binary/autorun.inf
./binary/css/
./binary/dists/
./img
./list
./packages`

fichier tmp

`./binary/autorun.inf
./binary/css/debinstall-print.css
./binary/css/debinstall.css
./binary/dists/frozen
./binary/dists/lenny/
./binary/dists/stable
./binary/dists/testing
./binary/dists/unstable
./img
./list
./packages`

et ainsi de suite  
Si quelqu’un a une idée pour rendre ça plus propre, je suis preneur.

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/2 "2016-02-20T21:19:41Z")

</div>

Inspire toi de ça : [viewtopic.php?f=9&t=24054](http://forum.debian-fr.org/viewtopic.php?f=9&t=24054)

---

<div class="post-metadata">

**Author:** ![Niloo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/n/c37758/32.png) [@Niloo](https://www.debian-fr.org/u/Niloo)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/3 "2016-02-20T21:19:41Z")

</div>

commande find ?

---

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/4 "2016-02-20T21:19:42Z")

</div>

> [@](#):
>
> commande find ?

Pourquoi faire simple quand on peut faire compliqué ? Merci.

Pour le lien que tu m’as donné MisterFreez, les langages utilisés disposent tous d’un moyen de traiter les entrées une à une.

M’enfin je vais quand même essayer de le finir, ne serait-ce que pour progresser en awk, qui à l’air très sympahtique.

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/5 "2016-02-20T21:19:42Z")

</div>

Selon l’usage et si tu utilise une shell moderne (commendre bash \> 4 ou zsh) tu as les globing :  
ls -1 \*_/_

---

<div class="post-metadata">

**Author:** ![Niloo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/n/c37758/32.png) [@Niloo](https://www.debian-fr.org/u/Niloo)\
**Post date:** [Février 20, 2016, 9:19 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/6 "2016-02-20T21:19:48Z")

</div>

Tu peux traiter les données une à une avec find également, voir exemple :

```auto

```

Ce code va chercher tous les fichiers de plus de 14 jours dans le dossier défini dans $DIRBACKUP avec le motif défini et les supprimer.

---

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:20 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/7 "2016-02-20T21:20:16Z")

</div>

J’ai réussit à faire quelque chose d’un peu plus présentable, il en allait de mon honneur :

[code]#!/bin/sh

function process\_an\_entry {  
if [-d “$1”]; then  
ls -1 “$1” | sed “s%.\*%$1/&%” | read\_input  
else  
echo "$1"  
fi  
}

function read\_input {  
while read line; do  
process\_an\_entry "$line"  
done  
}

process\_an\_entry “$1”[/code]

Mais ce n’est pas encore parfait :

- si un nom de répertoire contient un pourcentage % , ça va provoquer une erreur chez sed
- si un répertoire contient un lien sybolique vers l’un de ses répertoires parents, ça créé une boucle infinie
- c’est moins rapide que la commande find :

[code]$ time find ./repertoire\_de\_52000\_elements  
[…]  
real 0m43.2s  
user 0m00.3s  
sys 0m42.9s

$ time ./lsrec.sh ./repertoire\_de\_52000\_elements  
[…]  
real 1m00.5s  
user 0m12.4s  
sys 0m50.5s[/code]

Les temps sont une moyenne de trois exécutions.

@MisterFreeze : j’ai essayé la syntaxe \*_/_ , elle me donne le même formatage que ls -R

---

<div class="post-metadata">

**Author:** ![Niloo](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/n/c37758/32.png) [@Niloo](https://www.debian-fr.org/u/Niloo)\
**Post date:** [Février 20, 2016, 9:20 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/8 "2016-02-20T21:20:28Z")

</div>

Pourquoi cherches tu à produire le même comportement d’une commande qui existe déjà ?

---

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:20 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/9 "2016-02-20T21:20:32Z")

</div>

C’est juste pour m’entrainer. Et comme j’était un peu mal à l’aise d’avoir posté un code trop sale, je me suis senti obligé de le corriger.  
Je ne pense pas que la commande [find](http://www.linux-kheops.com/doc/man/manfr/man-html-0.9/man1/find.1.html) soit fournie par POSIX (sinon ils l’auraient marqué sur linux-kheops). Ca me fait une version de remplacement au cas ou que find ne soit pas installé.

J’en profite pour mettre la nouvelle version :

[code]#!/bin/sh

function lsrec {  
while read path; do  
if [-d “$path”]; then  
ls “$path” | { while read name; do echo “$path/$name”; done;} | lsrec  
else  
echo "$path"  
fi  
done  
}

echo “$1” | lsrec[/code]

J’ai pu me débarasser de sed et donc du problème des pourcentages. Il ne reste plus qu’à trouver un moyen de ne pas suivre les liens symboliques pour arriver à quelque chose de potable.

---

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:22 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/10 "2016-02-20T21:22:18Z")

</div>

Et bien voilà, les liens symboliques ne sont plus suivis :

[code]#!/bin/sh

function lsrec {  
while read path; do  
if [-d “$path” -a ! -h “$path”]; then  
ls “$path” | { while read name; do echo “$path/$name”; done;} | lsrec  
else  
echo "$path"  
fi  
done  
}

echo “$1” | lsrec[/code]

Après la seule optimisation que je voit c’est de ne pas lancer la fonction récursive sur les feuilles.

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 20, 2016, 9:22 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/11 "2016-02-20T21:22:19Z")

</div>

Soit tu écris :

[code]#!/bin/bash

function lsrec {  
while read path; do  
if [-d “$path” -a ! -h “$path”]; then  
ls “$path” | { while read name; do echo “$path/$name”; done;} | lsrec  
else  
echo "$path"  
fi  
done  
}

echo “$1” | lsrec[/code]  
Soit :

[code]#!/bin/sh

lsrec () {  
while read path; do  
if [-d “$path” -a ! -h “$path”]; then  
ls “$path” | { while read name; do echo “$path/$name”; done;} | lsrec  
else  
echo "$path"  
fi  
done  
}

echo “$1” | lsrec[/code]  
Mais sinon c’est pas moche 🙂

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 20, 2016, 9:22 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/12 "2016-02-20T21:22:19Z")

</div>

> [@](#):
>
> @MisterFreeze : j’ai essayé la syntaxe \*_/_ , elle me donne le même formatage que ls -R

```auto

```

---

<div class="post-metadata">

**Author:** ![branch](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/b/ee7513/32.png) [@branch](https://www.debian-fr.org/u/branch)\
**Post date:** [Février 20, 2016, 9:23 UTC](https://www.debian-fr.org/t/obtenir-un-ls-r-formate-de-maniere-utilisable-avec-find/24249/13 "2016-02-20T21:23:02Z")

</div>

[quote=“MisterFreez”]Mais sinon c’est pas moche 🙂[/quote] ![:smt026](https://www.debian-fr.org/uploads/default/original/1X/2c22390a61f927d29d63f74b75da177f81922e05.gif)

Déjà merci pour cette façon de déclarer les fonctions, qui est plus portable (syntaxe Bourne).

Mais j’ai pas encore dit mon dernier mot :

Déjà on peut retirer l’une des deux boucles “while read” :

[code]#!/bin/sh

lsrec() {  
ls -U “$1” | while read name; do  
if [-d “$1/$name” -a ! -h “$1/$name”]  
then lsrec "$1/$name"  
else echo $1/$name  
fi  
done  
}

lsrec “$1”[/code]

Ca ne m’a pas fait gagner beaucoup en temps d’exécution. A mon avis, c’est l’utilisation du pipe qui est pénalisant.  
Un autre moyen de transmettre les résultats de la commande ls est d’utiliser une boucle for :

`lsrec() {
  for name in `ls -U "$1"`; do # pb
    pathname="$1/$name"
    if [-d "$pathname" -a ! -h "$pathname"]
      then lsrec "$pathname"
      else echo $pathname
    fi
  done
}`

Par contre ça bug lorsqu’un nom de répertoire contient un espace. C’est à cause de la ligne ou il y a marqué “# pb” : la substitution de `ls -U "$1"` ne conserve pas en un seul morceau les nom de fichier/répertoire avec un espace dedans (comme le fait ls d’habitude).  
Peut-être qu’en jouant sur la valeur de la variable IFS (internal field separator) ça peut améliorer la situation ?

Il y a aussi une solution alternative qui remplace “ls” par le mécanisme de substitution d’expressions régulières par le shell : si on compare uniquement le temps “système”, cette solution est plus rapide que la commande find : 42,5 sec pour find contre 39,5 sec pour ceci :

`lsrec() {
  for path in "$1"/*; do
    if [-d "$path" -a ! -h "$path"]
      then lsrec "$path"
      else echo $path
    fi
  done
}`

Mais bon le temps “utilisateur” est toujours 40 fois plus long que la commande find (0,25 sec pour find contre 9,6 sec pour le script …) et on perd l’avantage des options de ls (prendre les fichiers cachés ou non, ordre du tri … etc), donc c’est juste pour apprécier la concision.

Il faut que je trouve un moyen de régler le problème de découpage du résultat de ls.
