web-dev-qa-db-fra.com

script bash: {dir1, dir2, dir3} l'expansion ne fonctionne pas comme prévu à partir de la variable, pour choisir un fichier aléatoire

J'ai un problème, avec ce simple script (choisissez un fichier aléatoire):

#!/usr/bin/env bash
set -x
srcDir="/home/user/Desktop/wallPapers/{dir1,dir2,dir3}"
randomFile=$(find "$srcDir" -type f -iname "*.jpg" | shuf -n 1)
printf '[%s]\n' $randomFile
set +x

Le problème est que même si je peux taper ceci sur la ligne de commande (et fonctionne parfaitement bien):

find /home/user/Desktop/wallPapers/{dir1,dir2,dir3} -type f -iname "*.jpg"

Ensuite, les commandes set de débogage bash (set -x et + x) m'indiquent que, pour une raison quelconque, bash enferme la chaîne de répertoire avec des guillemets simples et remplace également les guillemets doubles par des guillemets simples.

./script.sh
+ srcDir='/home/user/Desktop/wallPapers/{dir1,dir2,dir3}'
++ find '/home/user/Desktop/wallPapers/{dir1,dir2,dir3}' -type f -iname '"*.jpg"'
find: ‘/home/user/Desktop/wallPapers/{dir1,dir2,dir3}’: No such file or directory
+ randomFile=
+ printf '[%s]\n'
[]
+ set +x

Je comprends, c'est ce que voit bash, lorsque le script s'exécute:

find '/home/user/Desktop/wallPapers/{dir1,dir2,dir3}' -type -iname '*.jpg'

Et cela provoque le message "No such file or directory", très très ennuyeux ... Je ne comprends pas, pourquoi il insère ces guillemets simples, je veux des guillemets doubles à la place, tout comme sur la ligne de commande ... Quelqu'un pourrait-il expliquer, je serais heureux pour cela, merci!

6
Okay Dokey

L'expansion d'accolade ne se produit pas dans une affectation de variable, comme expliqué ici:

Pourquoi les préfixes tilde se développent avant l'affectation, mais pas les accolades

Dans d'autres contextes, les citations auraient également empêché l'expansion de l'accolade.

Même si vous parvenez à obtenir srcDir à développer dans une liste de répertoires, en le citant à nouveau dans la commande find, il sera traité comme un seul argument au lieu de 3 chemins distincts.

La bonne façon de faire cela dans bash est probablement d'utiliser un tableau:

#!/usr/bin/env bash
set -x
srcDir=("/home/user/Desktop/wallPapers/"{dir1,dir2,dir3})
randomFile=$(find "${srcDir[@]}" -type f -iname "*.jpg" | shuf -n 1)
printf '[%s]\n' "$randomFile"
set +x
11
steeldriver

Comme d'autres l'ont déjà souligné, les citations empêchent l'expansion de l'accolade. Vous pouvez simplifier votre script pour simplement:

#!/usr/bin/env bash
printf '[%s]\n' "$(find /home/terdon/Desktop/wallPapers/{dir1,dir2,dir3} -type f -iname "*.jpg" | shuf -n 1)"

Ou, si vos noms de fichiers peuvent contenir des caractères de nouvelle ligne:

#!/usr/bin/env bash
printf '[%s]\n' "$(find /home/terdon/Desktop/wallPapers/{dir1,dir2,dir3} -type f -iname "*.jpg" -print0| shuf -zn 1)"

Si vous voulez quelque chose qui peut s'exécuter sur des répertoires et des types de fichiers arbitraires, essayez ceci:

#!/usr/bin/env bash

targetFilePattern="$1"
shift
declare -a targetDirs=("$@")
echo "find ${targetDirs[@]} -type f -iname '$targetFilePattern' | shuf -n 1"
randomFile=$(find "${targetDirs[@]}" -type f -iname "$targetFilePattern" | shuf -n 1)
echo "$randomFile"

Vous pouvez ensuite l'exécuter en tant que:

printRandomFile '*jpg' /home/terdon/Desktop/wallPapers/{dir1,dir2,dir3}

Ou même

printRandomFile '*jpg' /home/terdon/Desktop/wallPapers/{dir1,dir2,dir3,'a dir with a space'}
4
terdon

L'expansion d'accolade n'est pas effectuée à l'intérieur de guillemets doubles. Il y a une question en double à ce sujet quelque part. En outre, utilisez l'indicateur -printf pour rechercher, la substitution de commandes n'est pas nécessaire, vous pouvez donc le faire

find /home/user/Desktop/wallPapers/{dir1,dir2,dir3} -type f -iname "*.jpg" -printf '[%f]\n' | shuf -n1
3