Dites si j'ai écrit un programme avec la ligne suivante:
int main(int argc, char** argv)
Maintenant, il sait quels arguments de ligne de commande lui sont transmis en vérifiant le contenu de argv
.
Le programme peut-il détecter le nombre d'espaces entre les arguments? Comme quand je les tape dans bash:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
L'environnement est un Linux moderne (comme Ubuntu 16.04), mais je suppose que la réponse devrait s'appliquer à tous les systèmes compatibles POSIX.
Réponses:
Il n'est pas significatif de parler d '"espaces entre les arguments"; c'est un concept de coquille.
Le travail d'un shell est de prendre des lignes entières d'entrée et de les former en tableaux d'arguments avec lesquels démarrer les commandes. Cela peut impliquer l'analyse de chaînes entre guillemets, l'expansion de variables, les caractères génériques de fichier et les expressions tilde, etc. La commande est lancée avec un
exec
appel système standard , qui accepte un vecteur de chaînes.Il existe d'autres façons de créer un vecteur de chaînes. De nombreux programmes bifurquent et exécutent leurs propres sous-processus avec des appels de commande prédéterminés - dans ce cas, il n'y a jamais de "ligne de commande". De même, un shell graphique (de bureau) peut démarrer un processus lorsqu'un utilisateur fait glisser une icône de fichier et la dépose sur un widget de commande - là encore, il n'y a pas de ligne textuelle pour avoir des caractères "entre" les arguments.
En ce qui concerne la commande invoquée, ce qui se passe dans un shell ou un autre processus parent / précurseur est privé et caché - nous ne voyons que le tableau de chaînes que le standard C spécifie qui
main()
peut accepter.la source
tar cf texts.tar *.txt
le programme tar obtient deux arguments et doit développer le second (*.txt
) lui-même. Beaucoup de gens ne réalisent pas comment cela fonctionne vraiment avant de commencer à écrire leurs propres scripts / programmes qui gèrent les arguments.En général, non. L'analyse de la ligne de commande est effectuée par le shell qui ne met pas la ligne non analysée à la disposition du programme appelé. En fait, votre programme peut être exécuté à partir d'un autre programme qui a créé l'argv non pas en analysant une chaîne mais en construisant un tableau d'arguments par programme.
la source
execve(2)
.Non, ce n'est pas possible, sauf si les espaces font partie d' un argument.
La commande accède aux arguments individuels à partir d'un tableau (sous une forme ou une autre selon le langage de programmation) et la ligne de commande réelle peut être enregistrée dans un fichier d'historique (si elle est tapée à une invite interactive dans un shell contenant des fichiers d'historique), mais est jamais transmis à la commande sous quelque forme que ce soit.
Toutes les commandes sous Unix sont finalement exécutées par l'une des
exec()
familles de fonctions. Ceux-ci prennent le nom de la commande et une liste ou un tableau d'arguments. Aucun d'eux ne prend une ligne de commande telle que tapée à l'invite du shell. Lasystem()
fonction le fait, mais son argument de chaîne est ensuite exécuté parexecve()
, qui, encore une fois, prend un tableau d'arguments plutôt qu'une chaîne de ligne de commande.la source
hello
etworld
est littéralement des espaces entre les deux arguments.hello
etworld
est littéralement fournir le deuxième des trois arguments.En général, ce n'est pas possible, comme l'ont expliqué plusieurs autres réponses.
Cependant, les shells Unix sont des programmes ordinaires (et ils interprètent la ligne de commande et la globlent , c'est-à-dire développant la commande avant de la faire
fork
&execve
pour elle). Voir cette explication surbash
les opérations shell . Vous pouvez écrire votre propre shell (ou patcher un shell de logiciel libre existant , par exemple GNU bash ) et l'utiliser comme shell (ou même comme shell de connexion, voir passwd (5) & shells (5) ).Par exemple, vous pourriez avoir votre propre programme shell mettre la ligne de commande complète dans une variable d'environnement (imaginez
MY_COMMAND_LINE
par exemple) -ou utiliser tout autre type de communication inter-processus pour transmettre la ligne de commande du shell au processus enfant-.Je ne comprends pas pourquoi vous voudriez faire cela, mais vous pourriez coder un shell se comportant de cette manière (mais je recommande de ne pas le faire).
BTW, un programme pourrait être démarré par un programme qui n'est pas un shell (mais qui fait fork (2) puis execve (2) , ou juste un
execve
pour démarrer un programme dans son processus actuel). Dans ce cas, il n'y a pas de ligne de commande du tout, et votre programme pourrait être démarré sans commande ...Notez que vous pourriez avoir un système Linux (spécialisé) sans aucun shell installé. C'est bizarre et inhabituel, mais possible. Vous devrez ensuite écrire un programme d' initialisation spécialisé en démarrant d'autres programmes selon vos besoins - sans utiliser de shell mais en faisant des appels
fork
&execve
system.Lire aussi Systèmes d'exploitation: trois éléments faciles et n'oubliez pas qu'il
execve
s'agit pratiquement toujours d'un appel système (sous Linux, ils sont répertoriés dans syscalls (2) , voir aussi intro (2) ) qui réinitialise l' espace d'adressage virtuel (et quelques autres choses) du processus qui le fait.la source
argv[0]
pour le nom du programme et les éléments restants pour les arguments sont des spécifications POSIX et ne peuvent pas être modifiés. Un environnement d'exécution pourrait spécifierargv[-1]
la ligne de commande, je suppose ...execve
documentation. Vous ne pouvez pas l'utiliserargv[-1]
, c'est un comportement indéfini de l'utiliser.execvepluscmd
avec un paramètre supplémentaire (ou convention argv), le syscall construit un vecteur d'argument pour main qui contient un pointeur sur la ligne de commande avant le pointeur sur le nom du programme, puis passe l'adresse du pointeur sur le nom du programme commeargv
lors de l'appel du programmemain
...sh
. Ce n'est donc pas nouveau.Vous pouvez toujours dire à votre shell d'indiquer aux applications quel code shell mène à leur exécution. Par exemple, avec
zsh
, en passant ces informations dans la$SHELL_CODE
variable d'environnement en utilisant lepreexec()
hook (printenv
utilisé comme exemple, vous utiliseriezgetenv("SHELL_CODE")
dans votre programme):Tous ceux-ci s'exécuteraient
printenv
comme:Permet
printenv
de récupérer le code zsh qui a conduit à l'exécution deprintenv
ces arguments. Ce que vous voudriez faire de ces informations ne m'est pas clair.Avec
bash
, la fonctionnalité la plus proche dezsh
spreexec()
utiliserait son$BASH_COMMAND
dans unDEBUG
piège, mais notez que celabash
fait un certain niveau de réécriture dans cela (et en particulier refacteurs certains des espaces utilisés comme délimiteur) et qui est appliqué à chaque commande (enfin, certains) exécutez, pas la ligne de commande entière comme entrée à l'invite (voir aussi l'functrace
option).Voyez comment certains des espaces qui sont des délimiteurs dans la syntaxe du langage shell ont été compressés en 1 et comment la ligne de commande complète n'est pas toujours transmise à la commande. Donc probablement pas utile dans votre cas.
Notez que je ne conseillerais pas de faire ce genre de chose, car vous risquez de divulguer des informations sensibles à chaque commande comme dans:
divulguerait ce secret à la fois
wc
etuntrustedcmd
.Bien sûr, vous pourriez faire ce genre de chose pour d'autres langues que le shell. Par exemple, en C, vous pouvez utiliser certaines macros qui exportent le code C qui exécute une commande vers l'environnement:
Exemple:
Voyez comment certains espaces ont été condensés par le pré-processeur C comme dans le cas bash. Dans la plupart, sinon toutes les langues, la quantité d'espace utilisée dans les délimiteurs ne fait aucune différence, il n'est donc pas surprenant que le compilateur / interprète prenne une certaine liberté avec eux ici.
la source
BASH_COMMAND
ne contenait pas les arguments de séparation d'espaces d'origine, donc ce n'était pas utilisable pour la demande littérale de l'OP. Cette réponse inclut-elle une démonstration dans un cas ou dans l'autre pour ce cas d'utilisation particulier?J'ajouterai simplement ce qui manque dans les autres réponses.
Non
Voir d'autres réponses
Peut-être, en quelque sorte
Il n'y a rien qui puisse être fait dans le programme, mais il y a quelque chose qui peut être fait dans le shell lorsque vous exécutez le programme.
Vous devez utiliser des guillemets. Donc au lieu de
vous devez en faire une
Cela transmettra un seul argument au programme, avec tous les espaces. Il y a une différence entre les deux, le second est littéral, exactement la chaîne telle qu'elle apparaît (sauf qu'elle
'
doit être tapée comme\'
). Le premier interprétera certains caractères, mais divisé en plusieurs arguments. Voir les citations du shell pour plus d'informations. Donc pas besoin de réécrire le shell, les concepteurs du shell y ont déjà pensé. Cependant, comme il s'agit désormais d'un seul argument, vous devrez en faire plus en passant dans le programme.Option 2
Passez les données via stdin. Il s'agit de la manière normale d'obtenir de grandes quantités de données dans une commande. par exemple
ou
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(Les italiques sont la sortie du programme)
la source
./myprog␣"␣␣␣␣␣aaa␣␣␣␣␣␣bbb"
exécute (généralement dans un processus enfant) le fichier stocké dans./myprog
et lui transmet deux arguments:./myprog
et␣␣␣␣␣aaa␣␣␣␣␣␣bbb
(argv[0]
etargc[1]
,argc
étant 2) et comme dans les OP, l'espace qui sépare ces deux arguments n'est transmis d'aucune façon àmyprog
.