N'y a-t-il pas un moyen de protéger les espaces dans l'expansion backtick (ou $ (...))?
Non, il n'y en a pas. Pourquoi donc?
Bash n'a aucun moyen de savoir ce qui doit être protégé et ce qui ne devrait pas l'être.
Il n'y a pas de tableaux dans le fichier / pipe Unix. Ce n'est qu'un flux d'octets. La commande à l'intérieur de ``
ou $()
génère un flux, que bash avale et traite comme une chaîne unique. À ce stade, vous n'avez que deux choix: le mettre entre guillemets, pour le conserver sous forme de chaîne, ou le mettre nu, afin que bash le divise en fonction de son comportement configuré.
Donc, ce que vous devez faire si vous voulez un tableau est de définir un format d'octet qui a un tableau, et c'est ce que les outils aiment xargs
et find
font: si vous les exécutez avec l' -0
argument, ils fonctionnent selon un format de tableau binaire qui termine les éléments avec l'octet nul, ajoutant la sémantique au flux d'octets autrement opaque.
Malheureusement, bash
ne peut pas être configuré pour fractionner les chaînes sur l'octet nul. Merci à /unix//a/110108/17980 de nous avoir montré quezsh
.
xargs
Vous voulez que votre commande s'exécute une fois, et vous avez dit que cela xargs -0 -n 10000
résout votre problème. Ce n'est pas le cas, cela garantit que si vous avez plus de 10000 paramètres, votre commande s'exécutera plus d'une fois.
Si vous voulez qu'il soit strictement exécuté une fois ou échoue, vous devez fournir l' -x
argument et un -n
argument plus grand que l' -s
argument (vraiment: suffisamment grand pour qu'un tas d'arguments de longueur nulle plus le nom de la commande ne tiennent pas la -s
taille). ( man xargs , voir extrait ci-dessous)
Le système sur lequel je suis actuellement a une pile limitée à environ 8M, voici donc ma limite:
$ printf '%s\0' -- {1..1302582} | xargs -x0n 2076858 -s 2076858 /bin/true
xargs: argument list too long
$ printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true
(no output)
frapper
Si vous ne voulez pas impliquer une commande externe, la boucle en lecture-lecture alimentant un tableau, comme indiqué dans /unix//a/110108/17980 , est le seul moyen pour bash de diviser les choses à l'octet nul.
L'idée de se procurer le script ( . ... "$@" )
pour éviter la limite de taille de pile est cool (je l'ai essayé, ça marche!), Mais probablement pas important dans des situations normales.
L'utilisation d'un fd spécial pour le tube de processus est importante si vous voulez lire autre chose depuis stdin, mais sinon vous n'en aurez pas besoin.
Ainsi, la façon la plus simple "native", pour les besoins quotidiens des ménages:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
Si vous aimez que votre arbre de processus soit propre et agréable à regarder, cette méthode vous permet de le faire exec mynonscript "${files[@]}"
, ce qui supprime le processus bash de la mémoire, en le remplaçant par la commande appelée. xargs
restera toujours en mémoire pendant l'exécution de la commande appelée, même si la commande ne s'exécute qu'une seule fois.
Ce qui parle contre la méthode bash native est la suivante:
$ time { printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true; }
real 0m2.014s
user 0m2.008s
sys 0m0.172s
$ time {
args=()
while IFS= read -rd '' arg; do
args+=( "$arg" )
done < <(printf '%s\0' -- $(echo {1..1302581}))
/bin/true "${args[@]}"
}
bash: /bin/true: Argument list too long
real 107m51.876s
user 107m38.532s
sys 0m7.940s
bash n'est pas optimisé pour la gestion des tableaux.
homme xargs :
-n max-args
Utilisez au maximum des arguments max-args par ligne de commande. Moins d'arguments que max-args seront utilisés si la taille (voir l'option -s) est dépassée, sauf si l'option -x est donnée, auquel cas xargs se fermera.
-s max-chars
Utilisez au maximum des caractères max-chars par ligne de commande, y compris la commande et les arguments initiaux et les valeurs nulles de fin aux extrémités des chaînes d'argument. La plus grande valeur autorisée dépend du système et est calculée comme la limite de longueur d'argument pour exec, moins la taille de votre environnement, moins 2048 octets de marge. Si cette valeur est supérieure à 128 Ko, 128 Ko sont utilisés comme valeur par défaut; sinon, la valeur par défaut est le maximum. 1 Ko est de 1024 octets.
-X
Quittez si la taille (voir l'option -s) est dépassée.
IFS="
, la nouvelle ligne,"
). Mais est-il nécessaire d'exécuter le script sur tous les noms de fichiers? Sinon, envisagez d'utiliser find it pour exécuter le script de chaque fichier.