Les variables comme $ 0 et $ 1 sont-elles des variables d'environnement / shell?

17

Il existe des variables dans la coquille comme $0, $1, $2, $?, etc.

J'ai essayé d'imprimer le shell et les variables d'environnement à l'aide de la commande suivante:

set

Mais ces variables n'étaient pas dans la liste.

Donc, fondamentalement, ces variables ne sont pas considérées comme des variables shell / environnement, non? (même si pour les sortir, vous devez les faire précéder d'un $, comme vous le faites avec les variables shell / environnement)

user7681202
la source
3
Les paramètres de position ne sont pas des variables. Vous ne pouvez pas export 3vous transformer $3en variable d'environnement. Vous ne pouvez pas unset 3; et vous ne pouvez pas attribuer $3une nouvelle valeur à l'aide de 3=val.
Kaz

Réponses:

25

Les variables sont l'une des trois variétés distinctes de paramètres dans le shell.

  1. Une variable est un paramètre dont le nom est un identifiant de shell valide; commence par _ou une lettre, suivie de zéro ou plusieurs lettres, chiffres ou _.
  2. Les position paramètres sont les paramètres numérotés $1, $2...
  3. Les paramètres spéciaux ont tous des noms à un seul caractère, et mis à part $0, ce sont tous des caractères de ponctuation différents.

set affiche uniquement les variables du shell.

Un sous-ensemble des variables du shell sont les variables d'environnement, dont les valeurs sont soit héritées de l'environnement au démarrage du shell, soit créées en définissant l' exportattribut sur un nom valide.

chepner
la source
1
Notez que setaffiche tous les paramètres dans zsh(pas $ 1, $ 2 ... mais $ *, $ @) et les fonctions dans bash et bosh. Certains shells comme ksh93 et ​​les anciennes versions des vars env de sortie de tiret qui n'étaient pas mappés à des variables de shell. ( env 1=foo ksh -c setimprimerait 1=foo)
Stéphane Chazelas
11

Variables d'environnement vs paramètres positionnels

Avant de commencer à discuter du $INTEGERtype de variables, nous devons comprendre ce qu'elles sont réellement et comment elles diffèrent des variables d'environnement. Des variables telles que $INTEGERles paramètres de position sont appelées. Ceci est décrit dans la norme POSIX (Portable Operating System Interface), section 2.1 (accent sur le mien):

  1. Le shell exécute une fonction (voir Commande de définition de fonction), intégrée (voir Utilitaires intégrés spéciaux), un fichier exécutable ou un script, donnant les noms des arguments sous forme de paramètres positionnels numérotés de 1 à n, et le nom de la commande (ou dans le cas d'une fonction dans un script, le nom du script) comme paramètre positionnel numéroté 0 (voir Recherche et exécution de commandes).

En revanche, des variables telles que $HOMEet $PATHsont des variables d'environnement. Leur définition est décrite dans la section 8 de la norme :

Les variables d'environnement définies dans ce chapitre affectent le fonctionnement de plusieurs utilitaires, fonctions et applications. Il existe d'autres variables d'environnement qui ne présentent d'intérêt que pour des utilitaires spécifiques. Les variables d'environnement qui s'appliquent à un seul utilitaire sont définies dans le cadre de la description de l'utilitaire.

Remarquez leur description. Les paramètres de position sont censés apparaître devant une commande, c'est-à-dire command positional_arg_1 positional_arg_2.... Ils sont destinés à être fournis par l'utilisateur pour indiquer à la commande quoi faire spécifiquement. Lorsque vous le faites echo 'Hello' 'World', il imprimera les chaînes Helloet World, car ce sont des paramètres de position pour echo- les choses sur lesquelles vous souhaitez echoopérer. Et echoest construit de telle sorte qu'il comprend les paramètres de position comme des chaînes à imprimer (à moins qu'ils ne soient l'un des indicateurs optionnels comme -n). Si vous faites cela avec une commande différente, il pourrait ne pas comprendre quoi HelloetWorldest parce qu'il attend peut-être un certain nombre. Notez que les paramètres positionnels ne sont pas "hérités" - un processus enfant ne connaît pas les paramètres positionnels du parent sauf s'il est explicitement transmis au processus enfant. Souvent, vous voyez des paramètres positionnels passés avec des scripts wrapper - ceux qui peuvent vérifier l'instance déjà existante d'une commande ou ajouter des paramètres positionnels supplémentaires à la commande réelle qui sera appelée.

En revanche, les variables d'environnement sont censées affecter plusieurs programmes. Ce sont des variables d' environnement , car elles sont définies en dehors du programme lui-même (plus d'informations ci-dessous). Certaines variables d'environnement telles que HOMEou PATHont un format spécifique, une signification spécifique, et elles auront la même signification pour chaque programme. HOMELa variable aura la même signification pour un utilitaire externe comme pour /usr/bin/findvotre shell (et par conséquent pour un script) - c'est le répertoire personnel du nom d'utilisateur sous lequel le processus s'exécute. Notez que les variables d'environnement peuvent être utilisées pour tenir compte d'un comportement de commande spécifique, par exempleUIDLa variable d'environnement peut être utilisée pour vérifier si le script s'exécute avec les privilèges root ou non et se ramifier à des actions spécifiques en conséquence. Les variables d'environnement sont héritables - les processus enfants obtiennent une copie de l'environnement du parent. Voir aussi Si les processus héritent de l'environnement du parent, pourquoi avons-nous besoin d'exporter?

En bref, la principale distinction est que les variables d'environnement sont définies en dehors de la commande et ne sont pas censées être modifiées (généralement), tandis que les paramètres de position sont des choses qui sont censées être traitées par la commande et changent.


Pas seulement des concepts shell

Ce que j'ai remarqué dans les commentaires, c'est que vous mélangez terminal et shell, et je vous recommande vraiment de lire sur les vrais terminaux qui étaient autrefois des appareils physiques. De nos jours, le "terminal" auquel nous nous référons généralement, cette fenêtre avec un fond noir et du texte vert est en fait un logiciel, un processus. Terminal est un programme qui exécute un shell, tandis que shell est également un programme mais celui qui lit ce que vous tapez pour exécuter (c'est-à-dire s'il s'agit d'un shell interactif; les shells non interactifs sont des scripts et des sh -c 'echo foo'types d'appels). Plus d'informations sur les obus ici .

Il s'agit d'une distinction importante, mais il est également important de reconnaître que le terminal est un programme et adhère donc aux mêmes règles d'environnement et de paramètres de position. Votre gnome-terminaldémarrage commencera à regarder votre SHELLvariable d'environnement et générera le shell par défaut approprié pour vous, sauf si vous spécifiez une autre commande avec -e. Disons que j'ai changé mon shell par défaut en ksh - gnome-terminal apparaîtra alors à la kshplace de bash. C'est également un exemple de la façon dont l'environnement est utilisé par les programmes. Si je dis explicitement à gnome-terminalavec -ed'exécuter un shell spécifique - il le fera, mais ce ne sera pas permanent. En revanche, l'environnement est censé être essentiellement inchangé (plus à ce sujet plus tard).

Donc, comme vous pouvez le voir, les variables d'environnement et de position sont toutes les deux des propriétés d'un processus / commande, pas seulement un shell. En ce qui concerne les scripts shell, ils suivent également le modèle défini par le langage de programmation C. Prenons par exemple la mainfonction C qui ressemble généralement à

int main(int argc, char **argv)

, où argcest le nombre d'arguments de ligne de commande et argvest effectivement un tableau de paramètres de ligne de commande, et puis il y a une environfonction (sous Linux c'est man -e 7 environ) pour accéder à des choses comme le chemin du répertoire personnel de l'utilisateur, la liste des répertoires dans PATHlesquels nous pouvons rechercher des exécutables, etc. Les scripts shell sont également modélisés de la même manière. Dans la terminologie shell, nous avons des paramètres positionnels $1, $2et ainsi de suite, tandis que $#est le nombre de paramètres positionnels. Et alors $0? C'est le nom de l'exécutable lui-même, qui est également modélisé à partir du langage de programmation C - argv[0]serait le nom de votre "exécutable" C. Et cela est assez vrai pour la plupart des langages de programmation et de script .

Coquilles interactives vs non interactives

J'ai déjà fait allusion à la distinction entre les shells interactifs et non interactifs . L'invite où vous tapez des commandes - c'est interactif, il interagit avec l'utilisateur. En revanche, lorsque vous avez un script shell ou que vous exécutez bash -c''ce n'est pas interactif.

Et c'est là que la distinction devient importante. Le shell que vous exécutez déjà est un processus qui a été généré avec des paramètres de position (pour le bashshell de connexion est un "... dont le premier caractère de l'argument zéro est un -, ou un a commencé avec l'option --login." ( Référence ) )

En revanche, les scripts et les shells lancés avec -coption peuvent tirer parti des arguments $1et $2. Par exemple,

$ bash -c 'echo $1; stat $2' sh 'Hello World' /etc/passwd
Hello World
  File: '/etc/passwd'
  Size: 2913        Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d  Inode: 6035604     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-08-12 14:48:37.125879962 -0600
Modify: 2017-08-12 14:48:37.125879962 -0600
Change: 2017-08-12 14:48:37.137879811 -0600
 Birth: -

Notez que j'y ai également utilisé sh, car une petite bizarrerie d' -coption est de prendre le premier paramètre positionnel et de l'affecter à $0, contrairement au nom typique du programme.

Une autre chose qui est importante à noter est que les paramètres de position sont ce que j'appelle "framable". Remarquez comment, nous avons d'abord lancé bashavec ses propres paramètres de position, mais ces paramètres de position sont devenus des paramètres pour echoet stat. Et chaque programme le comprend à sa manière. Si nous donnions à statune chaîne Hello Worldet qu'il n'y avait pas de fichier, Hello Worldcela produirait une erreur; bashla traite comme une simple chaîne mais stats'attend à ce que cette chaîne soit un nom de fichier existant. En revanche, tous les programmes conviennent que la variable d'environnement HOMEest un répertoire (à moins que le programmeur ne l'ait codé de manière déraisonnable).


Pouvons-nous jouer avec les variables d'environnement et les paramètres de position?

Techniquement, nous pouvons jouer avec les deux, mais nous ne devrions pas jouer avec les variables d'environnement, alors que nous devons souvent fournir des paramètres de position. Nous pouvons exécuter des commandes dans le shell en ajoutant une variable, par exemple:

$ hello=world bash -c 'echo $hello'
world

Nous pouvons également placer des variables dans l'environnement en utilisant simplement à export variable=valuepartir du shell ou du script. Ou nous pouvons exécuter une commande avec un environnement complètement vide avec env -c command arg1 arg2. Cependant, il n'est généralement pas recommandé de jouer avec l'environnement, en particulier en utilisant des variables majuscules ou en remplaçant des variables d'environnement déjà existantes. Notez que c'est recommandé mais pas standard.

Pour les paramètres de position, la façon de les définir est évidente, il suffit de les ajouter à la commande, mais il existe également des moyens de les définir autrement , ainsi que de modifier la liste de ces paramètres via la shiftcommande.

En conclusion, le but de ces deux est différent, et ils existent pour une raison. J'espère que les gens ont acquis un aperçu de cette réponse, et c'était amusant de la lire tout comme c'était pour moi d'écrire cette réponse.


Remarque sur la commande set

La setcommande, selon le manuel, se comporte ainsi (à partir du manuel bash, emphase ajoutée):

Sans options, le nom et la valeur de chaque variable shell sont affichés dans un format qui peut être réutilisé en entrée pour définir ou réinitialiser les variables actuellement définies.

En d'autres termes, setexamine les variables spécifiques au shell, dont certaines se trouvent dans l'environnement, par exemple HOME. En revanche, les commandes aiment envet printenvregardent la variable d'environnement réelle avec laquelle une commande s'exécute. Voir aussi ceci .

Sergiy Kolodyazhnyy
la source
"Dans le shell interactif, vous ne pouvez pas référencer $ 1, $ 2, etc.". Existe-t-il une référence directe pour cela? J'ai rencontré des cas étranges où ceux-ci sont définis dans un environnement de shell interactif et je ne sais pas si cela serait considéré comme «non standard» ou non.
user5359531
@ user5359531 Honnêtement, je ne sais pas très bien d'où je viens. Probablement parce qu'à l'époque où j'ai posté la réponse en 2017, j'ai probablement mentionné que vous ne pouviez pas faire quelque chose comme ça, 1="foo"mais plus tard, j'ai découvert que par définition POSIX, un "mot" (c'est-à-dire le nom d'un objet tel qu'une variable ou une fonction) ne peut pas commencer avec un chiffre (voir une question que j'ai postée sur le sujet ). Les paramètres de position font apparemment exception à cette règle.
Sergiy Kolodyazhnyy
@ user5359531 J'ai supprimé la partie de la réponse, car elle n'est pas particulièrement précise. Vous pouvez faire référence $1, $2et ainsi de suite dans le shell interactif, et en fait, cela se fait avec la setcommande, souvent pour contourner la /bin/shlimitation de ne pas avoir de tableaux. Merci d'avoir porté cela à mon attention. Je vais également modifier la réponse au cours des prochains jours, car elle a besoin d'un peu de polissage supplémentaire et d'une mise à jour.
Sergiy Kolodyazhnyy
Merci pour la clarification. Le problème que j'ai est que , avec conda, lorsque vous exécutez source conda/bin/activate, il vérifie si $1, $2etc., sont fixés, afin de déterminer si elle a été exécuté comme un script avec des arguments ou non. Cela finit par casser sur les systèmes qui ont ceux définis dans l'environnement interactif pour une raison quelconque. J'espère savoir si ce comportement non standard est une faille dans le système pour définir ces variables dans l'environnement interactif, ou dans le programme pour les utiliser pour déterminer s'il a été exécuté comme script.
user5359531
@ user5359531 Je vous suggère de déposer un rapport de bogue aux condadéveloppeurs ou à quiconque est l'auteur original d'un tel script, car la vérification des ${N}paramètres n'est certainement pas la bonne solution. Il y a des questions sur le même sujet ici et ici , et le moyen plus ou moins portable consiste à vérifier s'il ${0}est identique au nom du script, tout bashen ayant une variable d'environnement à cet effet
Sergiy Kolodyazhnyy
4

Les $1, $2, $3, ..., ${10}, ${11}variables sont appelées paramètres de position et sont couvertes dans la section du manuel bash3.4.1

3.4.1 Paramètres positionnels

Un paramètre positionnel est un paramètre désigné par un ou plusieurs chiffres, autre que le chiffre unique 0. Les paramètres positionnels sont attribués à partir des arguments du shell lors de son appel, et peuvent être réaffectés à l'aide de la commande set prédéfinie. Le paramètre positionnel N peut être référencé comme $ {N}, ou comme $ N lorsque N se compose d'un seul chiffre. Les paramètres de position ne peuvent pas être affectés à des instructions d'affectation. Les commandes intégrées set et shift sont utilisées pour les définir et les désactiver (voir Commandes intégrées de shell). Les paramètres positionnels sont temporairement remplacés lorsqu'une fonction shell est exécutée (voir Fonctions shell).

Lorsqu'un paramètre positionnel composé de plusieurs chiffres est développé, il doit être placé entre accolades.

Quant à $?et $0, ces paramètres spéciaux sont traités dans la section suivante3.4.2

3.4.2 Paramètres spéciaux

Le shell traite spécialement plusieurs paramètres. Ces paramètres ne peuvent être référencés; leur affectation n'est pas autorisée.

...

?

($?) Se développe jusqu'à l'état de sortie du dernier pipeline de premier plan exécuté.

0

($ 0) S'étend au nom du shell ou du script shell. Ceci est défini lors de l'initialisation du shell. Si Bash est invoqué avec un fichier de commandes (voir Scripts Shell), $ 0 est défini sur le nom de ce fichier. Si Bash est démarré avec l'option -c (voir Appel de Bash), alors $ 0 est défini sur le premier argument après la chaîne à exécuter, s'il y en a un. Sinon, il est défini sur le nom de fichier utilisé pour appeler Bash, comme indiqué par l'argument zéro.

Jesse_b
la source
4

$1, $2... sont des paramètres de position , ce ne sont pas des variables, encore moins des variables d'environnement.

Dans Bourne-shell comme la terminologie, $somethingest appelée paramètre expansion (couvre aussi ${something#pattern}et plus dans certains coquillages comme ${array[x]}, ${param:offset}, ${x:|y}et de nombreux opérateurs plus d'expansion).

Il existe différents types de paramètres:

  • des variables comme $foo,$PATH
  • paramètres de position ( $1, $2... les arguments reçus par votre script)
  • d' autres paramètres spéciaux comme $0, $-, $#, $*, $@, $$, $!, $?...

les noms de variables dans Bourne comme les shells doivent commencer par un caractère alphabétique (tout reconnu par les paramètres régionaux ou limité à a-zA-Z selon le shell) et souligner et suivi de zéro ou plusieurs caractères alphanumériques ou traits de soulignement.

Selon le shell, les variables peuvent avoir différents types (scalaire, tableau, hachage) ou avoir certains attributs (lecture seule, exporté, minuscule ...).

Certaines de ces variables sont créées par le shell ou ont une signification particulière à la coque (comme $OPTIND, $IFS, $_...)

Les variables shell qui ont l' attribut export sont automatiquement exportées en tant que variables d'environnement vers les commandes que le shell exécute.

La variable d'environnement est un concept distinct des variables shell. L'exportation d'une variable shell n'est pas le seul moyen de passer une variable d'environnement à l'exécution d'une commande.

VAR=foo
export VAR
printenv VAR

passera une VARvariable d'environnement à la printenvcommande (que nous indiquons pour imprimer son contenu), mais vous pouvez également faire:

env VAR=foo printenv VAR

ou:

perl -e '$ENV{VAR}="foo"; exec "printenv", "VAR"'

par exemple.

Les variables d'environnement peuvent avoir n'importe quel nom (peuvent contenir n'importe quel caractère mais =peuvent même être vides). Ce n'est pas une bonne idée de donner un nom qui n'est pas compatible avec celui d'un nom de variable shell de type Bourne à une variable d'environnement, mais c'est possible:

$ env '#+%=whatever' printenv '#+%'
whatever

Les shells mapperont les variables d'environnement qu'ils reçoivent en variables de shell uniquement pour les variables d'environnement dont le nom est des variables de shell valides (et dans certains shells, ignorez certaines spéciales comme $IFS).

Ainsi, alors que vous seriez en mesure de passer une 1variable d'environnement à une commande:

$ env '1=whatever' printenv 1
whatever

Cela ne signifie pas que l'appel d'un shell avec cette variable d'environnement définirait la valeur du $1paramètre:

$ env '1=whatever' sh -c 'echo "$1"' script-name foo bar
foo
Stéphane Chazelas
la source
3

Non, ce sont des paramètres du script. Par exemple, si vous appelez votre script comme:

mynicescript.sh one two three

puis à l'intérieur du script, ces paramètres seront disponibles en tant que

$1 = one
$2 = two
$3 = three

et le $ 0 est le nom du script lui-même.

Donc, lorsque vous êtes en dehors du script, ces variables ne sont pas disponibles (sauf $ 0, qui affiche / bin / bash - le shell lui-même).

Jaroslav Kucera
la source
"Donc, lorsque vous êtes en dehors du script, ces variables ne sont pas disponibles" Que voulez-vous dire par "en dehors du script" , car je peux voir les valeurs de ces variables dans mon terminal.
user7681202
2
@ user7681202: Lesquels pouvez-vous voir dans votre terminal? $0pointera vers votre processus terminal actuel (probablement bash) et $?est simplement le code de sortie du dernier processus.
Jesse_b
J'ai essayé d'exécuter l' gnome-terminalargument with ( gnome-terminal Hello World). Je pouvais voir $0, mais je ne pouvais pas voir $1et $2.
user7681202
@Jesse_b Merci, j'ai ajouté le contenu de 0 $ dans la réponse.
Jaroslav Kucera
1
@ user7681202 Le gnome-terminal n'est pas un shell, c'est un émulateur de terminal (comme xterm, konsole etc.). Le shell s'exécute à l'intérieur du terminal et il peut être bash / sh / zsh / tcsh et bien d'autres. Le script est un fichier exécutable avec un en-tête approprié (comme #! / Bin / bash) et un contenu interprétable par le shell spécifié dans le masque. Il utilise généralement le suffixe .sh
Jaroslav Kucera