Le bon outil est find
. Il fonctionne de manière récursive, ce qui résout votre principal problème. Vous pouvez exclure différents modèles si vous savez comment faire.
L'utilisation de base sera la suivante :
open "$(find . -type f | shuf -n1)"
Les nouvelles lignes dans les noms de fichiers ne fonctionneront pas. Vos outils peuvent ou non supporter des options non-POSIX qui permettent de passer des données terminées par NUL. Cet extrait fonctionne dans ma Debian :
find . -type f -print0 | shuf -z -n1
bien que si vous l'intégrez dans $(…)
les nouvelles lignes de fin (s'il y en a) seront toujours supprimées.
Pour exclure des noms, vous pouvez utiliser une syntaxe telle que ! -name .DS_Store
mais pour exclure des sous-répertoires entiers, il faut -prune
. Il y a des pièges :
- L'ordre des opérandes est important, par exemple
-prune
d'un répertoire doit se situer avant -type f
, -print
/ -print0
a généralement sa place à la fin.
- Logique "ou" (
-o
) nécessite souvent des parenthèses et ce n'est pas aussi intuitif que vous le souhaiteriez.
- Omettre
-print
/ -print0
peut vous donner plus de résultats que vous ne l'espérez. Dans le cas d'une logique complexe, il est bon d'inclure explicitement les éléments suivants -print
/ -print0
.
L'étude man 1 find
pour en savoir plus. Voici un exemple pratique qui exclut deux répertoires et deux modèles de noms :
find . \( -name dir1 -o -name "dir 2" \) -prune -o -type f ! \( -name "*.txt" -o -name "echo*" \) -print
Puisque vous avez besoin de $(…)
et je vous ai dit de citer correctement, vous devriez savoir que les guillemets à l'intérieur de $(…)
sont analysés séparément. Par exemple, ceci est correctement cité :
open "$(find . -type f ! -name "not this file" | shuf -n1)"
(comparer cette réponse, quirk 2 ).