web-dev-qa-db-fra.com

Comment passer des chaînes stockées dans des variables pour trouver une commande

Je dois passer les chaînes stockées dans une variable à une chaîne de commande de recherche.

Ce que je fais:

s="\( ! -path \"/path/to/1/Recycle Bin\" \) -or"
s="${s} \( ! -path \"/path/to/2/Recycle Bin\" \)"
exec=$(find "/path/to/Recycle Bin" -type d $s)

Résultat:

find: paths must precede expression: `\('

Si je fais:

exec=$(find "/path/to/Recycle Bin" -type d \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \))

Ça marche.

Qu'est-ce que je fais mal?

1
Bob

À mon humble avis, le seul moyen fiable de le faire est de faire appel à un tableau.

Les tableaux correctement cités vous permettent de construire une ligne de commande composée d'arguments analysables individuellement pouvant contenir des espaces.

Ex.

$ a=( find "/path/to/Recycle Bin" -type d )
$ a+=( \( ! -path "/path/to/1/Recycle Bin" )
$ a+=( -or ! -path "/path/to/2/Recycle Bin" \) )

Nous pouvons voir ce que le shell verra en imprimant les éléments individuels, un par ligne:

$ printf '%s\n' "${a[@]}"
find
/path/to/Recycle Bin
-type
d
(
!
-path
/path/to/1/Recycle Bin
-or
!
-path
/path/to/2/Recycle Bin
)
1
steeldriver

Si votre variable ne contient peut-être pas ce que vous pensez, utilisez

set -x

pour voir le résultat de chaque expansion.

Le shell traite les barres obliques inverses et les guillemets doubles dans les commandes find, mais il ne les traite pas de la même manière lorsqu'il développe votre variable ...

Sur la ligne de commande, nous écrivons:

find "/path/to/Recycle Bin" -type d \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)

Si vous avez exécuté set -x, vous pouvez voir ce que Bash en fait et ce qui est réellement passé à find:

+ find '/path/to/Recycle Bin' -type d '(' '!' -path '/path/to/1/Recycle Bin' ')' -or '(' '!' -path '/path/to/2/Recycle Bin' ')'

La suppression de la citation de votre variable ne se produit pas (vous insérez les caractères \ et " ici, de sorte que vous souhaitez les conserver, non? Ces caractères sont "issus de l'expansion" et non supprimé ) (\" est devenu " lorsque vous l'avez affecté comme vous le supposiez bien parce que, dans les guillemets doubles, la barre oblique inversée cite les caractères \$`"), mais la séparation des mots le fait:

$ find "/path/to/Recycle Bin" -type d $s
+ find '/path/to/Recycle Bin' -type d '\(' '!' -path '"/path/to/1/Recycle' 'Bin"' '\)' -or '\(' '!' -path '"/path/to/2/Recycle' 'Bin"' '\)'
find: paths must precede expression: \(

La citation de la variable corrige le mauvais fractionnement de Word mais empêche également tokenisation :

$ find "/path/to/Recycle Bin" -type d "$s"
+ find '/path/to/Recycle Bin' -type d '\( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)'
find: paths must precede expression: \( ! -path "/path/to/1/Recycle Bin" \) -or \( ! -path "/path/to/2/Recycle Bin" \)

Pratiquement tout ce que vous ferez avec ces deux chaînes aura pour résultat find de voir la mauvaise chose. Obtenir que votre variable ne contienne que et exactement une chaîne qui se développe en '(' '!' -path '/path/to/1/Recycle Bin' ')' -or '(' '!' -path '/path/to/2/Recycle Bin' ')' est une guerre de citations que je n'ai personnellement pas pu gagner.

Quelque chose comme ça fonctionnera bien

s="/path/to/1/Recycle Bin"
t="/path/to/2/Recycle Bin"
find "/path/to/Recycle Bin" -type d \( ! -path "$s" \) -or \( ! -path "$t" \)

Vous pouvez également vous en sortir si vous supprimez la nécessité de citer en n'utilisant pas de chemins avec des espaces - si vous supprimez les barres obliques inverses et les guillemets:

s="( ! -path /path/to/1/Trash ) -or"
s="${s} ( ! -path /path/to/2/Trash )"
find /path/to/Trash -type d $s

Peut-être que si vous expliquez pourquoi vous voulez le faire de cette façon, quelqu'un peut suggérer une meilleure solution de contournement.

0
Zanna