web-dev-qa-db-fra.com

Pourquoi mon script Shell s'étouffe sur les espaces ou autres caractères spéciaux?

Ou, un guide d'introduction à la gestion robuste des noms de fichiers et autres passages de chaînes dans les scripts Shell.

J'ai écrit un script Shell qui fonctionne bien la plupart du temps. Mais il s'étouffe sur certaines entrées (par exemple sur certains noms de fichiers).

J'ai rencontré un problème tel que le suivant:

  • J'ai un nom de fichier contenant un espace hello world, et il a été traité comme deux fichiers distincts hello et world.
  • J'ai une ligne d'entrée avec deux espaces consécutifs et ils se sont réduits à un dans l'entrée.
  • Les espaces blancs de début et de fin disparaissent des lignes d'entrée.
  • Parfois, lorsque l'entrée contient l'un des caractères \[*?, ils sont remplacés par du texte qui est en fait le nom des fichiers.
  • Il y a une apostrophe ' (ou un guillemet double ") dans l'entrée et les choses sont devenues bizarres après ce point.
  • Il y a une barre oblique inverse dans l'entrée (ou: j'utilise Cygwin et certains de mes noms de fichiers ont le style Windows \ séparateurs).

Que se passe-t-il et comment y remédier?

Utilisez toujours des guillemets doubles autour des substitutions de variables et des substitutions de commandes: "$foo", "$(foo)"

Si vous utilisez $foo Sans guillemets, votre script s'étouffera en entrée ou en paramètres (ou en sortie de commande, avec $(foo)) contenant des espaces ou \[*?.

Là, vous pouvez arrêter de lire. Eh bien, voici quelques autres:

  • read - Pour lire l'entrée ligne par ligne avec le code intégré read, utilisez while IFS= read -r line; do …
    Simple read traite spécialement les contre-obliques et les espaces.
  • xargs - Évitez xargs. Si vous devez utiliser xargs, faites-le xargs -0. Au lieu de find … | xargs, préférez find … -exec ….
    xargs traite spécialement les espaces blancs et les caractères \"'.

Cette réponse s'applique aux shells de style Bourne/POSIX (sh, ash, dash, bash, ksh, mksh, yash…). Les utilisateurs de Zsh devraient l'ignorer et lire la fin de Quand est-il nécessaire de citer deux fois? à la place. Si vous voulez tout le détail, lire la norme ou le manuel de votre Shell.


Notez que les explications ci-dessous contiennent quelques approximations (déclarations qui sont vraies dans la plupart des conditions mais qui peuvent être affectées par le contexte environnant ou par la configuration).

Pourquoi dois-je écrire "$foo"? Que se passe-t-il sans les guillemets?

$foo Ne signifie pas "prenez la valeur de la variable foo". Cela signifie quelque chose de beaucoup plus complexe:

  • Tout d'abord, prenez la valeur de la variable.
  • Fractionnement de champs: traitez cette valeur comme une liste de champs séparés par des espaces et créez la liste résultante. Par exemple, si la variable contient foo * bar ​, Le résultat de cette étape est la liste à 3 éléments foo, *, bar.
  • Génération de nom de fichier: traitez chaque champ comme un glob, c'est-à-dire comme un modèle générique, et remplacez-le par la liste des noms de fichiers qui correspondent à ce modèle. Si le modèle ne correspond à aucun fichier, il n'est pas modifié. Dans notre exemple, cela se traduit par la liste contenant foo, suivie de la liste des fichiers dans le répertoire courant, et enfin bar. Si le répertoire actuel est vide, le résultat est foo, *, bar.

Notez que le résultat est une liste de chaînes. Il existe deux contextes dans la syntaxe Shell: le contexte de liste et le contexte de chaîne. Le fractionnement de champ et la génération de nom de fichier ne se produisent que dans le contexte d'une liste, mais c'est la plupart du temps. Les guillemets doubles délimitent un contexte de chaîne: toute la chaîne entre guillemets est une chaîne unique, à ne pas fractionner. (Exception: "$@" Pour développer la liste des paramètres de position, par exemple "$@" Équivaut à "$1" "$2" "$3" S'il y a trois paramètres de position. Voir Quelle est la différence entre $ * et $ @? )

Il en va de même pour la substitution de commande avec $(foo) ou avec `foo`. D'un autre côté, n'utilisez pas `foo`: Ses règles de citation sont étranges et non portables, et tous les shells modernes supportent $(foo) qui est absolument équivalent sauf pour avoir des règles de citation intuitives.

La sortie de la substitution arithmétique subit également les mêmes extensions, mais ce n'est normalement pas un problème car elle ne contient que des caractères non extensibles (en supposant que IFS ne contient pas de chiffres ou -).

Voir Quand un guillemet double est-il nécessaire? pour plus de détails sur les cas où vous pouvez omettre les guillemets.

À moins que vous ne vouliez dire que tout ce rigmarole se produit, n'oubliez pas de toujours utiliser des guillemets doubles autour des substitutions de variables et de commandes. Faites attention: laisser de côté les guillemets peut conduire non seulement à des erreurs mais aussi à trous de sécurité .

Comment traiter une liste de noms de fichiers?

Si vous écrivez myfiles="file1 file2", Avec des espaces pour séparer les fichiers, cela ne peut pas fonctionner avec des noms de fichiers contenant des espaces. Les noms de fichiers Unix peuvent contenir n'importe quel caractère autre que / (Qui est toujours un séparateur de répertoire) et des octets nuls (que vous ne pouvez pas utiliser dans les scripts Shell avec la plupart des shells).

Même problème avec myfiles=*.txt; … process $myfiles. Lorsque vous faites cela, la variable myfiles contient la chaîne de 5 caractères *.txt, Et c'est lorsque vous écrivez $myfiles Que le caractère générique est développé. Cet exemple fonctionnera réellement jusqu'à ce que vous changiez votre script en myfiles="$someprefix*.txt"; … process $myfiles. Si someprefix est défini sur final report, Cela ne fonctionnera pas.

Pour traiter une liste de tout type (comme les noms de fichiers), placez-la dans un tableau. Cela nécessite mksh, ksh93, yash ou bash (ou zsh, qui n'a pas tous ces problèmes de citation); un shell POSIX simple (tel que ash ou dash) n'a pas de variables de tableau.

myfiles=("$someprefix"*.txt)
process "${myfiles[@]}"

Ksh88 a des variables de tableau avec une syntaxe d'attribution différente set -A myfiles "someprefix"*.txt (Voir variable d'assignation sous un environnement ksh différent si vous avez besoin de la portabilité ksh88/bash). Les shells de style Bourne/POSIX ont un seul tableau, le tableau de paramètres positionnels "$@" Que vous définissez avec set et qui est local à une fonction:

set -- "$someprefix"*.txt
process -- "$@"

Qu'en est-il des noms de fichiers commençant par -?

Sur une note connexe, gardez à l'esprit que les noms de fichiers peuvent commencer par un - (Tiret/moins), que la plupart des commandes interprètent comme dénotant une option. Certaines commandes (comme sh, set ou sort) acceptent également des options commençant par +. Si vous avez un nom de fichier qui commence par une partie variable, assurez-vous de passer -- Avant, comme dans l'extrait ci-dessus. Cela indique à la commande qu'il a atteint la fin des options, donc tout ce qui suit est un nom de fichier même s'il commence par - Ou +.

Vous pouvez également vous assurer que les noms de vos fichiers commencent par un caractère autre que -. Les noms de fichiers absolus commencent par / Et vous pouvez ajouter ./ Au début des noms relatifs. L'extrait de code suivant transforme le contenu de la variable f en un moyen "sûr" de faire référence au même fichier qui garantit de ne pas commencer par - Ni +.

case "$f" in -* | +*) "f=./$f";; esac

Enfin, sachez que certaines commandes interprètent - Comme signifiant entrée standard ou sortie standard, même après --. Si vous devez vous référer à un fichier réel nommé -, Ou si vous appelez un tel programme et que vous ne voulez pas qu'il lise depuis stdin ou écrive vers stdout, assurez-vous de réécrire - comme ci-dessus. Voir Quelle est la différence entre "du -sh *" et "du -sh ./*"? pour plus de détails.

Comment stocker une commande dans une variable?

"Commande" peut signifier trois choses: un nom de commande (le nom en tant qu'exécutable, avec ou sans chemin d'accès complet, ou le nom d'une fonction, intégrée ou alias), un nom de commande avec des arguments ou un morceau de code Shell. Il existe donc différentes manières de les stocker dans une variable.

Si vous avez un nom de commande, stockez-le simplement et utilisez la variable avec des guillemets comme d'habitude.

command_path="$1"
…
"$command_path" --option --message="hello world"

Si vous avez une commande avec des arguments, le problème est le même qu'avec une liste de noms de fichiers ci-dessus: c'est une liste de chaînes, pas une chaîne. Vous ne pouvez pas simplement placer les arguments dans une seule chaîne avec des espaces entre les deux, car si vous le faites, vous ne pouvez pas faire la différence entre les espaces qui font partie des arguments et les espaces qui séparent les arguments. Si votre Shell a des tableaux, vous pouvez les utiliser.

cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"

Et si vous utilisez un Shell sans tableaux? Vous pouvez toujours utiliser les paramètres de position, si cela ne vous dérange pas de les modifier.

set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"

Que faire si vous devez stocker une commande Shell complexe, par exemple avec des redirections, des tuyaux, etc.? Ou si vous ne souhaitez pas modifier les paramètres de position? Ensuite, vous pouvez créer une chaîne contenant la commande et utiliser la fonction intégrée eval.

code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"

Notez les guillemets imbriqués dans la définition de code: les guillemets simples '…' Délimitent un littéral de chaîne, de sorte que la valeur de la variable code est la chaîne /path/to/executable --option --message="hello world" -- /path/to/file1. La fonction intégrée eval indique au Shell d'analyser la chaîne passée en argument comme si elle apparaissait dans le script, donc à ce stade, les guillemets et le canal sont analysés, etc.

L'utilisation de eval est délicate. Réfléchissez bien à ce qui est analysé quand. En particulier, vous ne pouvez pas simplement insérer un nom de fichier dans le code: vous devez le citer, comme vous le feriez s'il était dans un fichier de code source. Il n'y a aucun moyen direct de le faire. Quelque chose comme code="$code $filename" Se casse si le nom de fichier contient un caractère spécial Shell (espaces, $, ;, |, <, >, Etc.). code="$code \"$filename\"" S'arrête toujours sur "$\`. Même code="$code '$filename'" Se casse si le nom de fichier contient un '. Il y a deux solutions.

  • Ajoutez une couche de guillemets autour du nom du fichier. La façon la plus simple de le faire est d'ajouter des guillemets simples autour de lui et de remplacer les guillemets simples par '\''.

    quoted_filename=$(printf %s. "$filename" | sed "s/'/'\\\\''/g")
    code="$code '${quoted_filename%.}'"
    
  • Conservez l'expansion des variables à l'intérieur du code, afin qu'elle soit recherchée lorsque le code est évalué, et non lorsque le fragment de code est généré. C'est plus simple mais ne fonctionne que si la variable est toujours présente avec la même valeur au moment où le code est exécuté, pas par ex. si le code est construit dans une boucle.

    code="$code \"\$filename\""
    

Enfin, avez-vous vraiment besoin d'une variable contenant du code? La façon la plus naturelle de donner un nom à un bloc de code est de définir une fonction.

Quoi de neuf avec read?

Sans -r, read autorise les lignes de continuation - il s'agit d'une seule ligne logique d'entrée:

hello \
world

read divise la ligne d'entrée en champs délimités par des caractères dans $IFS (Sans -r, La barre oblique inverse les échappe également). Par exemple, si l'entrée est une ligne contenant trois mots, read first second third Définit first sur le premier mot d'entrée, second sur le deuxième mot et third au troisième mot. S'il y a plus de mots, la dernière variable contient tout ce qui reste après avoir défini les précédents. Les espaces blancs avant et arrière sont coupés.

La définition de IFS sur la chaîne vide évite tout découpage. Voir Pourquoi `while IFS = read` est-il utilisé si souvent, au lieu de` IFS =; while read..`? pour une explication plus longue.

Quel est le problème avec xargs?

Le format d'entrée de xargs est constitué de chaînes séparées par des espaces qui peuvent éventuellement être entre guillemets simples ou doubles. Aucun outil standard ne produit ce format.

L'entrée de xargs -L1 Ou xargs -l Est presque une liste de lignes, mais pas tout à fait - s'il y a un espace à la fin d'une ligne, la ligne suivante est une ligne de continuation.

Vous pouvez utiliser xargs -0 Le cas échéant (et le cas échéant: GNU (Linux, Cygwin), BusyBox, BSD, OSX, mais ce n'est pas dans POSIX). C'est sûr, car les octets nuls ne peuvent pas apparaître dans la plupart des données, en particulier dans les noms de fichiers. Pour produire une liste de noms de fichiers séparés par des nuls, utilisez find … -print0 (ou vous pouvez utiliser find … -exec … comme expliqué ci-dessous) .

Comment traiter les fichiers trouvés par find?

find … -exec some_command a_parameter another_parameter {} +

some_command Doit être une commande externe, il ne peut pas s'agir d'une fonction Shell ou d'un alias. Si vous devez appeler un shell pour traiter les fichiers, appelez explicitement sh.

find … -exec sh -c '
  for x do
    … # process the file "$x"
  done
' find-sh {} +

J'ai une autre question

Parcourez la balise quoting sur ce site, ou Shell ou Shell-script . (Cliquez sur "en savoir plus…" pour voir quelques conseils généraux et une liste de questions courantes sélectionnées à la main.) Si vous avez recherché et que vous ne trouvez pas de réponse, demandez loin .

Bien que la réponse de Gilles soit excellente, je conteste son point principal

Utilisez toujours des guillemets doubles autour des substitutions de variables et des substitutions de commandes: "$ foo", "$ (foo)"

Lorsque vous commencez avec un shell de type Bash qui divise Word, oui, bien sûr, le conseil sûr est toujours d'utiliser des guillemets. Cependant, le fractionnement de Word n'est pas toujours effectué

§ Découpage de mots

Ces commandes peuvent être exécutées sans erreur

foo=$bar
bar=$(a command)
logfile=$logdir/foo-$(date +%Y%m%d)
PATH=/usr/local/bin:$PATH ./myscript
case $foo in bar) echo bar ;; baz) echo baz ;; esac

Je n'encourage pas les utilisateurs à adopter ce comportement, mais si quelqu'un comprend bien quand le fractionnement de Word se produit, il devrait pouvoir décider lui-même quand utiliser des guillemets.

26
Steven Penny

Pour autant que je sache, il n'y a que deux cas dans lesquels il est nécessaire de citer des extensions, et ces cas impliquent les deux paramètres spéciaux de Shell "$@" et "$*" - qui sont spécifiés pour se développer différemment lorsqu'ils sont placés entre guillemets. Dans tous les autres cas (à l'exclusion, peut-être, des implémentations de tableaux spécifiques à Shell) le comportement d'une extension est configurable - il existe des options pour cela.

Cela ne veut pas dire, bien sûr, que les doubles guillemets doivent être évités - au contraire, c'est probablement la méthode la plus pratique et la plus robuste pour délimiter une expansion que le Shell a à offrir. Mais, je pense, comme des alternatives ont déjà été exposées de manière experte, c'est un excellent endroit pour discuter de ce qui se passe lorsque le Shell augmente une valeur.

Le Shell, dans son cœur et son âme (pour ceux qui en ont), est un interpréteur de commandes - c'est un analyseur, comme un grand, interactif, sed. Si votre instruction Shell est étouffement on espace blanc ou similaire, cela est très probablement dû au fait que vous n'avez pas entièrement compris le processus d'interprétation de Shell - en particulier comment et pourquoi il traduit une instruction d'entrée en une commande exploitable. Le travail du Shell est de:

  1. accepter l'entrée

  2. interpréter et diviser correctement en entrée symbolisée mots

    • entrée mots sont les éléments de syntaxe du shell tels que $Word ou echo $words 3 4* 5

    • mots sont toujours divisés sur des espaces - c'est juste de la syntaxe - mais seulement les caractères d'espaces littéraux servis au Shell dans son fichier d'entrée

  3. développez-les si nécessaire en plusieurs champs

    • champs résulte de Word extensions - ils constituent la commande exécutable finale

    • sauf "$@", $IFSdivision du champ et expansion du nom de chemin une entrée Word doit toujours correspondre à un seul champ.

  4. puis pour exécuter la commande résultante

    • dans la plupart des cas, cela implique de transmettre les résultats de son interprétation sous une forme ou une autre

Les gens disent souvent que le Shell est un colle, et, si cela est vrai, alors ce que c'est coller est une liste d'arguments - ou champs - à un processus ou à un autre quand il execs les. La plupart des shells ne gèrent pas bien l'octet NUL - voire pas du tout - et c'est parce qu'ils se divisent déjà dessus. Le Shell doit execbeaucoup et il doit le faire avec un tableau d'arguments délimité NUL qu'il remet au noyau du système à exec temps. Si vous deviez mélanger le délimiteur du Shell avec ses données délimitées, le Shell le ferait probablement foirer. Ses structures de données internes - comme la plupart des programmes - reposent sur ce délimiteur. zsh, notamment, ne fait pas foirer ça.

Et c'est là que $IFS entre. $IFS est un paramètre Shell toujours présent - et également paramétrable - qui définit comment le Shell doit diviser les extensions Shell de Word à champ - spécifiquement sur quelles valeurs ces champs devrait délimiter. $IFS divise les extensions Shell sur les délimiteurs autres que NUL - ou, en d'autres termes, le Shell substitue les octets résultant d'une expansion qui correspondent à ceux de la valeur de $IFS avec NUL dans ses tableaux de données internes. Quand vous le regardez comme ça, vous pouvez commencer à voir que chaque field-split L'expansion du shell est un $IFS- tableau de données délimité.

Il est important de comprendre que $IFS uniquement délimite des extensions qui sont pas déjà autrement délimitées - ce que vous pouvez faire avec "double citation. Lorsque vous citez une expansion, vous la délimitez en tête et au moins à la fin de sa valeur. Dans ces cas, $IFS ne s'applique pas car il n'y a aucun champ à séparer. En fait, une expansion entre guillemets doubles présente un comportement fractionnement de champ identique à une expansion sans guillemets lorsque IFS= est défini sur une valeur vide.

Sauf si cité, $IFS est lui-même un $IFS expansion Shell délimitée. Il prend par défaut une valeur spécifiée de <space><tab><newline> - qui présentent tous les trois des propriétés spéciales lorsqu'ils sont contenus dans $IFS. Alors que toute autre valeur pour $IFS est spécifié pour être évalué en un seul champ par expansion occurrence, $IFSespaces blancs - n'importe lequel de ces trois - est spécifié pour élider à un seul champ par expansion séquence et les séquences de début/fin sont entièrement élues. C'est probablement plus facile à comprendre via l'exemple.

slashes=///// spaces='     '
IFS=/; printf '<%s>' $slashes$spaces
<><><><><><     >
IFS=' '; printf '<%s>' $slashes$spaces
</////>
IFS=; printf '<%s>' $slashes$spaces
</////     >
unset IFS; printf '<%s>' "$slashes$spaces"
</////     >

Mais c'est juste $IFS - juste le séparateur de mots ou espaces blancs comme demandé, alors qu'en est-il des caractères spéciaux?

Le Shell - par défaut - étendra également certains jetons non cotés (tels que ?*[ comme indiqué ailleurs ici) en plusieurs champs lorsqu'ils apparaissent dans une liste. Cela s'appelle expansion du nom de chemin, ou globbing. C'est un outil incroyablement utile et, comme il se produit après fractionnement de champ dans l'ordre d'analyse du shell, il n'est pas affecté par $ IFS - champs généré par une extension de nom de chemin sont délimités en tête/queue des noms de fichiers eux-mêmes, que leur contenu contienne ou non des caractères actuellement dans $IFS. Ce comportement est activé par défaut - mais il est très facilement configuré autrement.

set -f

Cela demande au Shell not to glob. L'expansion du nom de chemin ne se produira pas au moins jusqu'à ce que ce paramètre soit en quelque sorte annulé - comme si le shell actuel est remplacé par un autre nouveau processus Shell ou ....

set +f

... est délivré au Shell. Les guillemets doubles - comme ils le font également pour $IFSdivision du champ - rend ce paramètre global inutile par expansion. Donc:

echo "*" *

... si l'extension du chemin est actuellement activée, cela produira probablement des résultats très différents par argument - car le premier ne s'étendra qu'à sa valeur littérale (le seul astérisque, c'est-à-dire pas du tout) et le second uniquement au même si le répertoire de travail actuel ne contient aucun nom de fichier qui pourrait correspondre à (et il correspond à presque tous). Cependant, si vous le faites:

set -f; echo "*" *

... les résultats des deux arguments sont identiques - le * ne se développe pas dans ce cas.

22
mikeserv

J'avais un grand projet vidéo avec des espaces dans les noms de fichiers et des espaces dans les noms de répertoire. Tandis que find -type f -print0 | xargs -0 fonctionne à plusieurs fins et sur différents shells, je trouve que l'utilisation d'un IFS personnalisé (séparateur de champ d'entrée) vous donne plus de flexibilité si vous utilisez bash. L'extrait ci-dessous utilise bash et définit IFS sur une nouvelle ligne seulement; à condition qu'il n'y ait pas de nouvelle ligne dans vos noms de fichiers:

(IFS=$'\n'; for i in $(find -type f -print) ; do
    echo ">>>$i<<<"
done)

Notez l'utilisation de parens pour isoler la redéfinition d'IFS. J'ai lu d'autres articles sur la façon de récupérer IFS, mais c'est plus simple.

De plus, définir IFS sur newline vous permet de définir des variables Shell à l'avance et de les imprimer facilement. Par exemple, je peux augmenter une variable V de manière incrémentielle en utilisant des sauts de ligne comme séparateurs:

V=""
V="./Ralphie's Camcorder/STREAM/00123.MTS,04:58,05:52,-vf yadif"
V="$V"$'\n'"./Ralphie's Camcorder/STREAM/00111.MTS,00:00,59:59,-vf yadif"
V="$V"$'\n'"next item goes here..."

et en conséquence:

(IFS=$'\n'; for v in $V ; do
    echo ">>>$v<<<"
done)

Maintenant, je peux "lister" le réglage de V avec echo "$V" en utilisant des guillemets doubles pour afficher les retours à la ligne. (Crédit à ce fil pour le $'\n' explication.)

3
Russ

La méthode d'utilisation de find directory -print0 | xargs -0 devrait gérer toutes les promotions. Cependant, il nécessite un PID par fichier/répertoire, ce qui peut entraîner un problème de performances.

Permettez-moi de décrire une autre méthode de gestion de fichiers robuste (et performante) que j'ai récemment rencontrée, qui convient si la sortie find doit être post-traitées en tant que données CSV séparées par des tabulations, par exemple par AWK. Dans un tel traitement, seuls les onglets et les sauts de ligne dans les noms de fichiers sont perturbateurs:

Le répertoire est analysé via find directory -printf '%P\t///\n'. Si le chemin ne contient pas d'onglets ou de sauts de ligne, cela conduit à un enregistrement avec deux champs CSV: le chemin lui-même et le champ contenant ///.

Si un onglet est contenu dans le chemin, il y aura trois champs: chemin fragment1, chemin fragment2 et le champ contenant ///.

Si une nouvelle ligne est contenue, il y aura deux enregistrements: le premier enregistrement contiendra le fragment de chemin1 et le deuxième enregistrement contiendra le fragment de chemin2 et le champ contenant ///.

Maintenant, le fait clé est que /// ne peut pas se produire naturellement dans les chemins. De plus, c'est une sorte d'évacuation étanche ou de terminaison.

Il est également possible d'écrire un programme (AWK) qui analyse la sortie find et, jusqu'à ce que il trouve ///, il rassemble les fragments en sachant qu'un nouveau champ est tabulé dans le chemin et que le nouvel enregistrement est un saut de ligne dans le chemin.

Les onglets peuvent être échappés en toute sécurité comme ///t et les sauts de ligne peuvent être échappés en toute sécurité comme ///n, encore une fois, en sachant que /// ne peut pas se produire naturellement dans les chemins de fichiers. Conversion ///t et ///n retour aux onglets et les sauts de ligne peuvent se produire à la fin, lorsqu'une sortie est générée à partir du traitement.

Oui, cela semble compliqué, mais l'indice est que seuls deux PID sont nécessaires: l'instance find et awk qui exécute l'algorithme décrit. Et c'est rapide.

L'idée n'est pas la mienne, je l'ai trouvée implémentée dans ce nouveau script bash (2019) pour la synchronisation des répertoires: Zaloha.sh . Ils ont un document qui décrit l'algorithme, en fait.

Je n'ai pas pu casser/étouffer ce programme par des caractères spéciaux dans les noms de fichiers. Il a même correctement traité les répertoires nommés newline et tab seul ...

0
user400462

Compte tenu de toutes les implications de sécurité mentionnées ci-dessus et en supposant que vous faites confiance et contrôlez les variables que vous développez, il est possible d'avoir plusieurs chemins avec des espaces en utilisant eval. Mais fais attention!

$ FILES='"a b" c'
$ eval ls $FILES
ls: a b: No such file or directory
ls: c: No such file or directory
$ FILES='a\ b c'
$ eval ls $FILES
ls: a b: No such file or directory
ls: c: No such file or directory
0
Mattias Wadman