D'après ce que je peux dire, printenv
affiche les variables d'environnement, mais pourquoi ne vois-je pas d'autres variables comme PS1
pour personnaliser l'invite de shell?
En quoi consiste exactement printenv
et pourquoi ne pas capter PS1
? Existe-t-il une commande de sortie plus complète faisant plus que printenv
?
En effet, PS1
n'est pas normalement exporté.
Les variables d'environnement sont utilisées pour définir l'environnement d'exécution des processus enfants. puisque PS1
n'a réellement de signification que dans un shell interactif, il n'est normalement pas importé de l'exporter - il ne s'agit que d'une simple variable shell .
Si vous démarrez un enfant interactif Shell , il lira et définira son PS1
à partir du fichier de ressources du shell, tel que ~/.bashrc
.
Si vous export PS1
, vous le verrez dans la sortie printenv
. Sinon, vous pouvez voir des variables Shell simples à l’aide de la bash intégrée set
comme décrit ici Comment lister tous les noms de variables et leurs valeurs actuelles?
Existe-t-il une commande de sortie plus complète faisant plus que
printenv
?
printenv
imprime seulement variables d’environnement , ce qui peut être considéré comme un avantage. Mais si vous souhaitez également imprimer des variables Shell, utilisez echo "$x"
(ou printf '%s\n' "$x"
, qui est plus robuste ) au lieu de printenv x
.
l'explication de steeldriver de ces questions est utile et correcte, mais je présente le sujet d'une autre manière ici.
printenv
est une commande externe - non intégrée à votre shell, mais un programme distinct de celui-ci. Il affiche ses propres variables d’environnement, qui sont celles qu’il hérite du shell que vous utilisez pour l’exécuter. Cependant, les shell ne transmettent pas toutes leurs variables à leurs environnements sous-processus '. Au lieu de cela, ils maintiennent une distinction entre les variables qui sont variables d'environnement et celles qui ne le sont pas. (Ceux qui ne le sont pas sont souvent appelés Variables Shell.)
Pour voir comment cela fonctionne, essayez ces commandes, qui sont incluses dans (
)
afin qu'elles agissent indépendamment 1 d'un autre. Individuellement, chacune de ces commandes fonctionne de la même manière lorsque vous l'exécutez sans le (
)
, mais les variables que vous créez dans les commandes précédentes existeraient toujours dans les commandes ultérieures. L'exécution des commandes dans les sous-shell empêche cela.
La création d'une nouvelle variable, puis l'exécution d'une commande externe, ne la transmet pas à l'environnement de la commande. Sauf dans le cas inhabituel où vous avez déjà une variable d'environnement x
, cette commande ne produit aucun résultat:
(x=foo; printenv x)
La variable is assignée dans le shell, cependant. Cette commande génère foo
:
(x=foo; echo "$x")
Le shell prend en charge la syntaxe pour transmettre une variable dans l'environnement d'une commande sans affectant l'environnement du shell en cours. Ceci génère foo
:
x=foo printenv x
(Cela fonctionne également dans un sous-shell, bien sûr --(x=foo printenv x)
--, mais je l'ai montré sans le (
)
car, lorsque vous utilisez cette syntaxe, rien n'est défini pour votre Shell actuel. commandes d'être affectés.)
Ceci affiche foo
, puis bar
:
(x=bar; x=foo printenv x; echo "$x")
Lorsque vous exportez une variable, celle-ci est automatiquement transmise aux environnements de toutes les commandes externes ultérieures exécutées à partir du même shell. La commande export
effectue cette opération. Vous pouvez l'utiliser avant de définir la variable, après l'avoir définie, ou même de définir la variable in la commande export
elle-même. Tous ces éléments impriment foo
:
(x=foo; export x; printenv x)
(export x; x=foo; printenv x)
(export x=foo; printenv x)
Il n'y a pas de commande unexport
. Même si vous pouvez exporter une variable avant de la définir, la désélectionner est également annulée, ce qui revient à dire que cela n’imprime rien, plutôt que d’afficher bar
:
(x=foo; export x; unset x; x=bar; printenv x)
Mais modification la valeur d'une variable après son exportation ne affecte la valeur exportée. Ceci affiche foo
, puis bar
:
(export x=foo; printenv x; x=bar; printenv x)
Comme d’autres processus, votre Shell hérite lui-même des variables d’environnement de son processus parent. Ces variables sont présentes initialement dans l'environnement de votre shell et elles sont automatiquement exportées - ou rester exportées, si vous choisissez de le penser de cette façon. Ceci imprime foo
(rappelez-vous, VAR=val cmd
exécute cmd
avec VAR
défini sur val
dans son environnement):
x=foo bash -c 'printenv x'
Les variables définies dans les processus enfants n'affectent pas le processus parent, même si elles sont exportées. Ceci affiche foo
(pas bar
):
(x=foo; bash -c 'export x=bar'; echo "$x")
Un sous-shell est aussi un processus enfant 2 ; ceci affiche également foo
:
(x=foo; (export x=bar); echo "$x")
Cela devrait expliquer pourquoi j'ai inclus la plupart de ces commandes dans (
)
pour les exécuter dans des sous-shell.
Les sous-coques sont spéciales, cependant. Contrairement aux autres sous-processus, tels que ceux créés lorsque vous exécutez une commande externe telle que printenv
ou bash
, n sous-shell hérite de la plupart de l'état de son shell parent . En particulier, les sous-shell héritent des variables non exportées . Tout comme (x=foo; echo "$x")
imprime foo
, il en va de même de (x=foo; (echo "$x"))
.
La variable non exportée n'est toujours pas exportée dans le sous-shell - à moins que vous ne l'exportiez - donc, tout comme (x=foo; printenv x)
n'imprime rien, il en va de même de (x=foo; (printenv x))
.
Un sous-shell est un type spécial de sous-processus qui est un shell. Tous les sous-processus qui sont des shells ne sont pas des sous-shells. Le shell créé en exécutant bash
est pas un sous-shell et il n'hérite pas des variables non exportées. Donc, cette commande affiche une ligne vide (car echo
imprime une nouvelle ligne même s’elle est appelée avec un argument vide):
(x=foo; bash -c 'echo "$x"')
PS1
n'est pas une variable d'environnement (et ne devrait normalement pas en être une)Enfin, en ce qui concerne les raisons pour lesquelles les variables d'invite telles que PS1
sont des variables Shell mais non des variables d'environnement, les raisons sont les suivantes:
PS1
à un nouveau shell échouera généralement car , le shell réinitialisera généralement PS1
. Le point 3 mérite un peu plus d'explications, mais si vous n'essayez jamais de transformer PS1
en variable d'environnement, vous n'avez probablement pas vraiment besoin de connaître les détails.
PS1
.Lorsqu'un Bash Shell non interactif démarre, il 3 nsetsPS1
. Ceci affiche une ligne vierge (pas foo
):
PS1=foo bash -c 'echo "$PS1"'
Pour vérifier qu'il est réellement non défini, et pas seulement défini, mais vide, vous pouvez exécuter ceci, qui affiche unset
:
PS1=foo bash -c 'if [[ -v PS1 ]]; then echo set; else echo unset; fi'
Pour vérifier que cela est indépendant d'un autre comportement au démarrage, vous pouvez essayer de passer toute combinaison de --login
, --norc
ou --posix
avant -c
, ou de définir BASH_ENV
sur le chemin d'accès à un script (par exemple, BASH_ENV=~/.bashrc PS1=foo bash ...
) ou ENV
si vous avez passé --posix
. En aucun cas, un shell non interactif Bash ne peut pas annuler PS1
.
Cela signifie que si vous exportez PS1
et exécutez un shell non interactif qui lui-même exécute un shell interactif, la valeur PS1
ne sera pas définie à l'origine. Pour cette raison - et aussi parce que d'autres shells autres que Bash (comme Ksh) ne se comportent pas tous de la même manière, et la façon dont vous écrivez PS1
pour Bash ne fonctionne pas toujours pour ces shells - je déconseille de tenter de faire de PS1
un variable d'environnement. Editez simplement ~/.bashrc
pour définir l'invite de votre choix.
PS1
.Inversement, si vous nset]PS1
et exécutez un interpréteur de commandes Bash interactif, même si vous l'empêchez d'exécuter des commandes à partir de scripts de démarrage en transmettant --norc
, il restera automatiquement set PS1
à valeur par défaut. Exécuter env -u PS1 bash --norc
vous donne un shell Bash interactif avec PS1
défini sur \s-\v\$
. Etant donné que Bash étend \s
au nom du shell et \v
au numéro de version, ceci affiche bash-4.3$
comme invite sur Ubuntu 16.04 LTS. Notez que définir la valeur de PS1
en tant que chaîne vide n'est pas la même chose que le désactiver. Comme expliqué ci-dessous, l'exécution de PS1= bash
vous donne un shell interactif avec un comportement de démarrage étrange. Évitez d'exporter PS1
lorsqu'il est défini sur la chaîne vide, pour une utilisation pratique, à moins que vous ne compreniez et souhaitiez ce comportement.
Toutefois, si vous définissez PS1
et exécutez un interpréteur de commandes Bash interactif - et qu'il ne soit pas annulé par un interpréteur de commandes non interactif - il conservera cette valeur ... jusqu'à ce qu'un script de démarrage tel que le /etc/profile
global (pour les shells de connexion) ou /etc/bash.bashrc
, ou votre ~/.profile
, ~/.bash_login
ou ~/.bash_profile
par utilisateur (tous pour les shells de connexion) ou ~/.bashrc
par l'utilisateur le réinitialise.
Même si vous éditez ces fichiers pour les empêcher de définir PS1
-- ce que, dans le cas de /etc/profile
et /etc/bash.bashrc
, je déconseille de toute façon, car ils affectent tous les utilisateurs - vous ne pouvez pas vraiment vous fier à cela. Comme indiqué ci-dessus, les shells interactifs démarrés à partir de shells non interactifs n'auront pas PS1
, sauf si vous devez le réinitialiser et le réexporter dans le shell non interactif. En outre, vous devriez réfléchir à deux fois avant de procéder car il est courant que le code Shell (y compris les fonctions Shell que vous avez définies) vérifie PS1
pour déterminer si le shell dans lequel il s'exécute est interactif ou non.
PS1
est un moyen courant de déterminer si le shell actuel est interactif.C’est pourquoi il est si important pour les shells Bash non interactifs 4 pour nset PS1
automatiquement. Comme dans la section 6.3.2 Ce Shell est-il interactif? du Manuel de référence de Bash dit:
Les scripts [S] tartup peuvent examiner la variable
PS1
; il est non défini dans les shells non interactifs et défini dans des shells interactifs.
Pour voir comment cela fonctionne, voir l'exemple ici. Ou consultez les utilisations du monde réel dans Ubuntu. Par défaut, /etc/profile
dans Ubuntu comprend:
if [ "$PS1" ]; then
if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then
# The file bash.bashrc already sets the default PS1.
# PS1='\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
/etc/bash.bashrc
, qui ne devrait absolument rien faire lorsque le shell est non interactif, a:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
Pour atteindre le même objectif, /etc/skel/.bashrc
, qui est copié dans les répertoires de base des utilisateurs lors de la création de leurs comptes (votre ~/.bashrc
est donc probablement similaire), a:
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
C’est l’autre moyen courant de vérifier si un shell est interactif: voyez si le texte obtenu par en développement le paramètre spécial-
(en écrivant $-
) contient la lettre i
. Habituellement, cela a exactement le même effet. Supposons toutefois que vous n’ayez pas modifié le code présenté ci-dessus qui apparaît par défaut dans les scripts de démarrage de Bash dans Ubuntu, et que:
PS1
en tant que variable d’environnement, etEnsuite, /etc/profile
(s'il s'agit d'un shell de connexion) ou /etc/bash.bashrc
n'exécutera pas les commandes qu'ils exécutent habituellement pour les shells interactifs. ~/.bashrc
le sera toujours.
Si vous souhaitez vérifier si un shell est interactif à l'aide de PS1
et obtenir la bonne réponse même si PS1
est défini mais vide, vous pouvez utiliser [[ -v PS1 ]]
ou [ -v PS1 ]
/test -v PS1
. Notez cependant que le mot clé [[
et le test -v
des constructions intégrées [
et test
sont propres à Bash. Tous les autres coquillages à la Bourne ne les acceptent pas. Donc, vous devriez not les utiliser dans des scripts tels que ~/.profile
et /etc/profile
qui pourraient s’exécuter dans d’autres shells (ou par un gestionnaire d’affichage lorsque vous vous connectez graphiquement), à moins que le script ne contienne autre chose qui vérifie quel shell est en cours d'exécution et n'exécute les commandes spécifiques à Bash que lorsque ce shell est Bash (par exemple, en cochant $BASH_VERSION
).
1 Cet article explique les sous-coques en détail. .2.4.3 Commandes de regroupement du manuel de référence de Bash explique la syntaxe (
)
.
2 Notez qu'il existe circonstances sous lesquelles les commandes exécutées dans des sous-shell, même avec la syntaxe (
)
, n'est pas utilisée. Par exemple, lorsque vous avez commandes séparées par |
dans un pipeline , Bash les exécute dans un sous-shell (à moins que lastpipe
option du shell soit défini).
3 Sauf pour sous-coquilles . On peut soutenir que cela n’est même pas une exception, car les sous-shell ne "démarrent" pas au sens habituel de ce que nous entendons par là. (Ils n'ont pas vraiment de comportement d'initialisation significatif.) Notez que lorsque vous exécutez bash
-- avec ou sans arguments - dans un shell Bash, cela crée un sous-processus qui est un shell, mais il s'agit de not = un sous-shell.
4 Notez que tous les obus - pas même tous les obus à la Bourne - se comportent de cette manière. Mais Bash le fait, et il est très courant que le code Bash, y compris le code dans les scripts de démarrage, l’utilise.