D'abord pour couper les réponses triviales mais inapplicables: je ne peux utiliser ni l' astuce find
+ xargs
ni ses variantes (comme find
avec -exec
) car j'ai besoin d'utiliser peu de telles expressions par appel. J'y reviendrai à la fin.
Maintenant, pour un meilleur exemple, considérons:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Comment puis-je transmettre ces arguments à program
?
Le faire ne fait pas l'affaire
$ ./program $(find -L some/dir -name \*.abc | sort)
échoue car program
obtient les arguments suivants:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Comme on peut le voir, le chemin avec l'espace a été divisé et program
considère qu'il s'agit de deux arguments différents.
Citez jusqu'à ce que cela fonctionne
Il semble que les utilisateurs novices comme moi, lorsqu'ils sont confrontés à de tels problèmes, ont tendance à ajouter des citations au hasard jusqu'à ce que cela fonctionne enfin - seulement ici, cela ne semble pas aider ...
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Étant donné que les guillemets empêchent la séparation des mots, tous les fichiers sont transmis en tant qu'argument unique.
Citation de chemins individuels
Une approche prometteuse:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
Les citations sont là, bien sûr. Mais ils ne sont plus interprétés. Ils ne sont qu'une partie des chaînes. Ainsi, non seulement ils n'ont pas empêché le fractionnement de mots, mais ils se sont aussi mis à discuter!
Changer IFS
Ensuite, j'ai essayé de jouer avec IFS
. Je préférerais find
avec -print0
et sort
avec de -z
toute façon - afin qu'ils n'aient aucun problème sur les "chemins câblés" eux-mêmes. Alors pourquoi ne pas forcer le mot à se scinder sur le null
personnage et tout avoir?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Donc, il se divise toujours sur l'espace et ne se divise pas sur le null
.
J'ai essayé de placer l' IFS
affectation à la fois $(…)
(comme indiqué ci-dessus) et avant ./program
. Aussi j'essayé d' autres syntaxe comme \0
, \x0
, à la \x00
fois cité avec '
et "
ainsi qu'avec et sans $
. Rien de tout cela ne semblait faire de différence…
Et là, je suis à court d'idées. J'ai essayé quelques autres choses, mais tout semblait se résumer aux mêmes problèmes que ceux énumérés.
Que pouvais-je faire d'autre? Est-ce faisable du tout?
Bien sûr, je pourrais faire program
accepter les modèles et faire des recherches lui-même. Mais c'est beaucoup de travail double tout en le fixant à une syntaxe spécifique. (Qu'en est-il de la fourniture de fichiers par un grep
par exemple?).
Je pourrais aussi faire program
accepter un fichier avec une liste de chemins. Ensuite, je peux facilement vider l' find
expression dans un fichier temporaire et fournir le chemin d'accès à ce fichier uniquement. Cela pourrait être pris en charge le long de chemins directs de sorte que si l'utilisateur a juste un chemin simple, il peut être fourni sans fichier intermédiaire. Mais cela ne semble pas agréable - il faut créer des fichiers supplémentaires et en prendre soin, sans parler de l'implémentation supplémentaire requise. (Du côté positif, cependant, cela pourrait être un sauvetage pour les cas où le nombre de fichiers en tant qu'arguments commence à causer des problèmes avec la longueur de la ligne de commande…)
À la fin, permettez-moi de vous rappeler à nouveau que les astuces find
+ xargs
(et similaires) ne fonctionneront pas dans mon cas. Pour simplifier la description, je ne montre qu'un seul argument. Mais mon vrai cas ressemble plus à ceci:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Donc, faire une xargs
recherche à partir d'une recherche me laisse encore comment gérer l'autre…
la source
mapfile
(ou son synonymereadarray
). Mais, il fonctionne!while
boucle ne vide pas le tableau. Ce qui signifie que si aucun fichier n'est trouvé, le tableau n'est pas défini. Alors que s'il est déjà défini, de nouveaux fichiers seront ajoutés (au lieu de remplacer les anciens). Il semble que l'ajoutdeclare -a ABC_FILES='()';
avantwhile
fasse l'affaire. (Tout en ajoutant simplementABC_FILES='()';
non.)< <
signifie ici? Est-ce la même chose que<<
? Je ne pense pas que le changer pour<<
produire une erreur de syntaxe ("jeton inattendu` ('"). Alors qu'est-ce que c'est et comment ça marche?ABC_FILES
. C'est bon. Mais il est utile de faire égalementABS_ARGS
qui est un tableau vide siABC_FILES
est vide ou bien c'est un tableau('--abc-files' "${ABC_FILES[@]}")
. De cette façon, plus tard, je peux l'utiliser comme ceci:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
et assurez-vous qu'il fonctionnera correctement quel que soit le groupe (le cas échéant) vide. Ou pour le dire différemment: cette façon--abc-files
(et--xyz-files
) ne sera fournie que si elle est suivie par un chemin réel.while read ... done < <(find blah)
est une redirection<
de shell normale à partir d'un fichier spécial créé par PROCESS SUBSTITUTION . Cela diffère de la tuyauteriefind blah | while read ... done
car le pipeline exécute lawhile
boucle dans un sous-shell, de sorte que les variables définies ne sont pas conservées pour les commandes suivantes.Vous pouvez utiliser IFS = newline (en supposant qu'aucun nom de fichier ne contient newline) mais vous devez le définir dans le shell externe AVANT la substitution:
Avec
zsh
mais pas,bash
vous pouvez également utiliser null$'\0'
. Même enbash
vous pourriez gérer la nouvelle ligne s'il y a un caractère suffisamment étrange qui n'est jamais utilisé commeCependant, cette approche ne gère pas la demande supplémentaire que vous avez faite dans les commentaires sur la réponse de @ steeldriver pour omettre les --afiles si find a est vide.
la source
IFS
séparationnull
?read -d ''
méthode utilisée dans les méthodes de Steeldriver est une chaîne vide et non une chaîne contenant un octet nul. (Et une option de commande n'est pas une var en tant que telle de toute façon.)set -o noglob
) avant d'utiliser cet opérateur split + glob (sauf danszsh
).$'\0'
et aussi que''
.Je ne suis pas sûr de comprendre pourquoi vous avez abandonné
xargs
.La chaîne
--xyz-files
n'est qu'un des nombreux arguments et il n'y a aucune raison de la considérer comme spéciale avant qu'elle ne soit interprétée par votre programme. Je pense que vous pouvez le passerxargs
parmi les deuxfind
résultats:la source
-print0
secondefind
. De plus, si je procède de cette façon, je mettrais--abc-files
aussiecho
le - juste pour la cohérence..abc
fichiers, il ne devrait pas y en avoir--abc-files
(comme avec.xyz
). La solution basée sur un tableau par Steeldriver nécessite également une logique supplémentaire, mais cette logique est triviale là-bas alors qu'elle pourrait ne pas être si triviale ici, détruisant le principal avantage de cette solution - la simplicité.xargs
n'essaiera jamais de fractionner les arguments et de créer quelques commandes au lieu d'une, à moins qu'il ne soit explicitement chargé de le faire avec les arguments-L
,--max-lines
(-l
),--max-args
(-n
) ou--max-chars
(-s
). Ai-je raison? Ou y a-t-il des défauts? Comme mon programme ne gérerait pas une telle division correctement et j'aurais préféré ne pas l'appeler ...-print0
- corrigé, merci. Je ne connais pas toutes les réponses mais je suis d'accord que ma solution rend difficile l'inclusion d'une logique supplémentaire. J'irais probablement avec des tableaux moi-même, maintenant quand je connais cette approche. Ma réponse n'était pas vraiment pour vous. Vous aviez déjà accepté l'autre réponse et j'ai supposé que votre problème était résolu. Je voulais juste souligner que vous pouvez transmettre des arguments de plusieurs sourcesxargs
, ce qui n'était pas évident à première vue. Vous pouvez le considérer comme une preuve de concept. Maintenant, nous connaissons tous peu d'approches différentes et nous pouvons consciemment choisir ce qui nous convient dans chaque cas particulier.--abc-files
). Mais vous avez raison - il est bon de connaître vos alternatives! Surtout que je pensais à tort que ce n'était pas possible.