Sind Variablen wie $ 0 und $ 1 Shell- / Umgebungsvariablen?

17

Es gibt Variablen in der Shell wie $0, $1, $2, $?etc.

Ich habe versucht, die Shell- und Umgebungsvariablen mit dem folgenden Befehl zu drucken:

set

Diese Variablen waren jedoch nicht in der Liste enthalten.

Im Grunde genommen werden diese Variablen also nicht als Shell- / Umgebungsvariablen betrachtet, oder? (Auch wenn Sie sie ausgeben möchten, müssen Sie ihnen $wie bei Shell- / Umgebungsvariablen ein voranstellen. )

user7681202
quelle
3
Die Positionsparameter sind keine Variablen. Sie können nicht export 3drehen $3in eine Umgebungsvariable. Sie können nicht unset 3; und Sie können mit keinen $3neuen Wert zuweisen 3=val.
Kaz

Antworten:

25

Variablen sind eine von drei verschiedenen Arten von Parametern in der Shell.

  1. Eine Variable ist ein Parameter, dessen Name eine gültige Shell-ID ist. beginnt mit _oder einem Buchstaben, gefolgt von null oder mehr Buchstaben, Zahlen oder _.
  2. Die Positionsparameter sind die nummerierten Parameter $1, $2...
  3. Die speziellen Parameter haben alle Einzelzeichennamen und bis auf die $0verschiedenen Interpunktionszeichen.

set Zeigt nur die Variablen der Shell an.

Eine Teilmenge der Shell-Variablen sind die Umgebungsvariablen, deren Werte entweder beim Starten der Shell von der Umgebung geerbt oder durch Setzen des exportAttributs auf einen gültigen Namen erstellt werden.

chepner
quelle
1
Beachten Sie, dass setalle Parameter in zsh(nicht $ 1, $ 2 ..., sondern $ *, $ @) und die Funktionen in bash und bosh angezeigt werden. Einige Shells wie ksh93 und ältere Versionen von Dash-Ausgabevariablen, die nicht Shell-Variablen zugeordnet wurden. ( env 1=foo ksh -c setwürde drucken 1=foo)
Stéphane Chazelas
11

Umgebungsvariablen vs Positionsparameter

Bevor wir mit der Erörterung $INTEGERvon Variablentypen beginnen, müssen wir verstehen, was sie wirklich sind und wie sie sich von Umgebungsvariablen unterscheiden. Variablen, wie sie als $INTEGERPositionsparameter bezeichnet werden. Dies ist im POSIX-Standard (Portable Operating System Interface), Abschnitt 2.1 (Hervorhebung) beschrieben:

  1. Die Shell führt eine Funktion (siehe Befehl zur Funktionsdefinition), eine integrierte Datei (siehe Spezielle integrierte Dienstprogramme) oder ein Skript aus und gibt die Namen der Argumente als Positionsparameter mit den Nummern 1 bis n und den Namen des Befehls an (oder im Fall einer Funktion innerhalb eines Skripts der Name des Skripts) als Positionsparameter mit der Nummer 0 (siehe Befehlssuche und -ausführung).

Im Gegensatz dazu sind Variablen wie $HOMEund $PATHUmgebungsvariablen. Ihre Definition ist in Abschnitt 8 der Norm beschrieben :

In diesem Kapitel definierte Umgebungsvariablen wirken sich auf den Betrieb mehrerer Dienstprogramme, Funktionen und Anwendungen aus. Es gibt andere Umgebungsvariablen, die nur für bestimmte Dienstprogramme von Interesse sind. Umgebungsvariablen, die nur für ein Dienstprogramm gelten, werden als Teil der Dienstprogrammbeschreibung definiert.

Beachten Sie deren Beschreibung. Positionsparameter sollen vor einem Befehl erscheinen, dh command positional_arg_1 positional_arg_2.... Sie sollen vom Benutzer bereitgestellt werden, um dem Befehl mitzuteilen, was speziell zu tun ist. Wenn Sie dies tun echo 'Hello' 'World', werden die Zeichenfolgen Hellound Worldausgedruckt, da dies Positionsparameter für echodie Dinge sind, mit denen Sie arbeiten möchten echo. Und echoist so aufgebaut, dass es Positionsparameter als zu druckende Zeichenfolgen versteht (es sei denn, sie sind eines der optionalen Flags wie -n). Wenn Sie dies mit einem anderen Befehl tun, wird möglicherweise nicht verstanden, was HelloundWorldliegt daran, dass es vielleicht eine nummer erwartet. Beachten Sie, dass Positionsparameter nicht "geerbt" werden - ein untergeordneter Prozess kennt die Positionsparameter des übergeordneten Prozesses nur, wenn sie ausdrücklich an den untergeordneten Prozess übergeben werden. Häufig werden Positionsparameter mit Wrapper-Skripten übergeben. Diese prüfen möglicherweise, ob bereits ein Befehl vorhanden ist, oder fügen dem aufzurufenden realen Befehl zusätzliche Positionsparameter hinzu.

Im Gegensatz dazu sollen Umgebungsvariablen mehrere Programme beeinflussen. Sie sind Umgebungsvariablen , da sie außerhalb des Programms selbst festgelegt sind (mehr dazu weiter unten). Bestimmte Umgebungsvariablen wie HOMEoder PATHhaben ein bestimmtes Format, eine bestimmte Bedeutung und bedeuten für jedes Programm dasselbe. HOMEVariable bedeutet dasselbe für ein externes Dienstprogramm wie /usr/bin/findoder Ihre Shell (und folglich für ein Skript) - es ist das Ausgangsverzeichnis des Benutzernamens, unter dem der Prozess ausgeführt wird. Beachten Sie, dass Umgebungsvariablen zum Beispiel verwendet werden können, um ein bestimmtes Befehlsverhalten zu berücksichtigenUIDDie Umgebungsvariable kann verwendet werden, um zu überprüfen, ob das Skript mit Root-Rechten ausgeführt wird oder nicht, und entsprechend zu bestimmten Aktionen zu verzweigen. Umgebungsvariablen sind vererbbar - untergeordnete Prozesse erhalten eine Kopie der übergeordneten Umgebung. Siehe auch Wenn Prozesse die Umgebung des übergeordneten Elements erben, warum müssen wir exportieren?

Kurz gesagt, der Hauptunterschied besteht darin, dass Umgebungsvariablen außerhalb des Befehls festgelegt werden und (normalerweise) nicht variiert werden sollen, während Positionsparameter Dinge sind, die vom Befehl verarbeitet werden sollen und sich ändern.


Nicht nur Shell-Konzepte

Was ich aus Kommentaren mitbekommen habe, ist, dass Sie Terminal und Shell vertauschen, und ich würde Ihnen wirklich empfehlen, über echte Terminals zu lesen , die früher physische Geräte waren. Heutzutage ist das "Terminal", auf das wir uns normalerweise beziehen, dieses Fenster mit schwarzem Hintergrund und grünem Text eigentlich Software, ein Prozess. Terminal ist ein Programm, das eine Shell ausführt, während Shell auch ein Programm ist, das jedoch das liest, was Sie eingeben, um es auszuführen (dh, wenn es sich um eine interaktive Shell handelt; nicht interaktive Shells sind Skripte und sh -c 'echo foo'Arten von Aufrufen). Mehr zu Muscheln hier .

Dies ist eine wichtige Unterscheidung, aber auch wichtig zu erkennen, dass das Terminal ein Programm ist und daher dieselben Umgebungsregeln und Positionsparameter einhält. Ihre gnome-terminalwann Blick auf Ihre gestartet SHELLUmgebungsvariable, und laichen die entsprechende Standard - Shell für Sie, wenn Sie mit einem anderen Befehl angeben -e. Sagen wir , ich meine Standard - Shell geändert ksh - gnome-terminal wird dann laichen kshstatt bash. Dies ist auch ein Beispiel dafür, wie die Umgebung von Programmen verwendet wird. Wenn ich ausdrücklich sagen , gnome-terminalmit -ebestimmt Shell auszuführen - es wird es tun, aber es wird nicht dauerhaft sein. Im Gegensatz dazu soll die Umgebung größtenteils unverändert bleiben (dazu später mehr).

Wie Sie sehen können, sind Umgebungsvariablen und Positionsvariablen beide Eigenschaften eines Prozesses / Befehls und nicht nur eine Shell. Wenn es um Shell-Skripte geht, folgen sie auch dem Modell, das von der Programmiersprache C festgelegt wurde. Nehmen wir zum Beispiel die C- mainFunktion, die normalerweise so aussieht

int main(int argc, char **argv)

, wo argcist die Anzahl der Befehlszeilenargumente und argvist effektiv Array von Befehlszeilenparametern, und dann gibt es eine environFunktion (unter Linux man -e 7 environ), um auf Dinge wie den Home-Verzeichnispfad des Benutzers, eine Liste von Verzeichnissen zuzugreifen, in PATHdenen wir nach ausführbaren Dateien suchen können, usw. In ähnlicher Weise werden auch Shell-Skripte modelliert. In Schale Terminologie haben wir Positionsparameter $1, $2usw., während die $#Anzahl der Positionsparameter ist. Was ist $0? Das ist der Name der ausführbaren Datei selbst, die ebenfalls aus der Programmiersprache C abgeleitet ist - argv[0]wäre der Name Ihrer ausführbaren C-Datei. Dies gilt für die meisten Programmier- und Skriptsprachen .

Interaktive vs nicht interaktive Shells

Eines der Dinge, die ich bereits angedeutet habe, ist die Unterscheidung zwischen interaktiven und nicht interaktiven Shells . Die Eingabeaufforderung, in der Sie Befehle eingeben - das ist interaktiv, es interagiert mit dem Benutzer. Im Gegensatz dazu, wenn Sie ein Shell-Skript haben oder es bash -c''nicht interaktiv ausführen .

Und hier wird Unterscheidung wichtig. Die Shell, die Sie bereits ausführen, ist ein Prozess, der mit Positionsparametern erzeugt wurde (für die bashAnmeldeshell ist dies einer "... dessen erstes Zeichen des Arguments Null ein - ist oder einer, der mit der Option --login gestartet wurde" ( Referenz ). )

Im Gegensatz dazu Skripte und Muscheln mit ins Leben gerufen -cOption nutzen können $1und $2Argumente. Zum Beispiel,

$ 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: -

Beachten Sie, dass ich dort auch verwendet habe sh, weil eine kleine Eigenheit der -cOption darin besteht, den ersten Positionsparameter zu nehmen und ihm zuzuweisen $0, im Gegensatz dazu, dass es normalerweise ein Name des Programms ist.

Eine andere wichtige Sache ist, dass Positionsparameter das sind, was ich "framable" nenne. Beachten Sie, wie wir zuerst bashmit eigenen Positionsparametern gestartet haben , aber diese Positionsparameter wurden zu Parametern für echound stat. Und jedes Programm versteht es auf seine Weise. Wenn wir stateine Zeichenfolge angeben Hello Worldund keine Datei vorhanden Hello Worldist, wird ein Fehler ausgegeben. bashbehandelt es wie eine einfache Zeichenfolge, staterwartet jedoch, dass es sich bei dieser Zeichenfolge um einen vorhandenen Dateinamen handelt. Im Gegensatz dazu stimmen alle Programme darin überein, dass die Umgebungsvariable HOMEein Verzeichnis ist (es sei denn, der Programmierer hat es in unvernünftiger Weise codiert).


Können wir mit Umgebungsvariablen und Positionsparametern herumspielen?

Technisch können wir mit beiden herumspielen, aber wir sollten nicht mit Umgebungsvariablen herumspielen, während wir oft Positionsparameter bereitstellen müssen. Wir können Befehle in der Shell ausführen, indem wir eine Variable voranstellen, zum Beispiel:

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

Wir können Variablen auch einfach export variable=valueaus der Shell oder dem Skript heraus in die Umgebung einfügen . Oder wir können einen Befehl mit völlig leerer Umgebung mit ausführen env -c command arg1 arg2. In der Regel wird jedoch nicht empfohlen, mit der Umgebung herumzuspielen, insbesondere wenn Sie Variablen in Großbuchstaben verwenden oder bereits vorhandene Umgebungsvariablen überschreiben. Beachten Sie, dass dies empfohlen wird, obwohl dies kein Standard ist.

Bei Positionsparametern ist die Art und Weise der Einstellung offensichtlich. Stellen Sie sie einfach vor den Befehl, aber es gibt auch Möglichkeiten, sie auf andere Weise einzustellen und die Liste dieser Parameter über den shiftBefehl zu ändern .

Zusammenfassend ist der Zweck dieser beiden unterschiedlich, und sie existieren aus einem Grund. Ich hoffe, die Leute haben einen Einblick in diese Antwort erhalten, und es hat Spaß gemacht, sie so zu lesen, wie es für mich war, diese Antwort zu schreiben.


Hinweis zum Befehl set

Der setBefehl verhält sich laut Handbuch wie folgt (aus Bash-Handbuch, Hervorhebung hinzugefügt):

Ohne Optionen werden der Name und der Wert jeder Shell-Variablen in einem Format angezeigt, das als Eingabe zum Festlegen oder Zurücksetzen der aktuell festgelegten Variablen verwendet werden kann.

Mit anderen Worten, es setwerden spezifische Variablen für die Shell betrachtet, von denen sich einige zum Beispiel zufällig in der Umgebung befinden HOME. Im Gegensatz dazu mögen envund printenvbetrachten Befehle die tatsächliche Umgebungsvariable, mit der ein Befehl ausgeführt wird. Siehe auch das .

Sergiy Kolodyazhnyy
quelle
"In der interaktiven Shell können Sie nicht auf $ 1, $ 2 usw. verweisen." Gibt es eine direkte Referenz dafür? Ich bin auf seltsame Fälle gestoßen, in denen diese in einer interaktiven Shell-Umgebung festgelegt sind, und ich bin mir nicht sicher, ob dies als "nicht standard" angesehen wird oder nicht.
user5359531
@ user5359531 Ehrlich gesagt bin ich mir nicht ganz sicher, woher ich das habe. Wahrscheinlich, weil ich in der Zeit, als ich die Antwort 2017 veröffentlichte 1="foo", darauf hingewiesen habe, dass man so etwas nicht kann, aber später stellte ich fest, dass nach POSIX-Definition ein "Wort" (das ist ein Name eines Objekts wie eine Variable oder eine Funktion) nicht beginnen kann mit einer Ziffer (siehe eine Frage, die ich zum Thema gestellt habe ). Positionsparameter sind offensichtlich eine Ausnahme von dieser Regel.
Sergiy Kolodyazhnyy
@ user5359531 Ich habe den Teil der Antwort entfernt, da er nicht besonders genau ist. Sie können in der interaktiven Shell auf und so weiter verweisen $1, $2und zwar mit setBefehlen, um die /bin/shEinschränkung zu umgehen , dass keine Arrays vorhanden sind. Vielen Dank, dass Sie mich darauf aufmerksam gemacht haben. Ich werde die Antwort in den nächsten Tagen auch bearbeiten, da es ein bisschen mehr Glanz und ein Update braucht.
Sergiy Kolodyazhnyy
Danke für die Klarstellung. Das Problem , das ich habe ist , dass mit conda, wenn Sie laufen source conda/bin/activate, es prüft , ob $1, $2etc. eingestellt werden , um zu bestimmen , ob sie als Skript mit Argumenten oder nicht ausgeführt wurde. Dies führt dazu, dass Systeme aus irgendeinem Grund nicht mehr funktionieren, auf denen diese in der interaktiven Umgebung eingestellt sind. Ich hoffe herauszufinden, ob dieses nicht standardmäßige Verhalten ein Fehler im System zum Festlegen dieser Variablen in der interaktiven Umgebung oder im Programm ist, mit dessen Hilfe festgestellt werden kann, ob sie als Skript ausgeführt wurden.
user5359531
@ user5359531 Ich würde vorschlagen, dass Sie einen Fehlerbericht an condaEntwickler oder an den ursprünglichen Autor eines solchen Skripts senden , da die Überprüfung auf ${N}Parameter definitiv der falsche Weg ist. Es gibt Fragen zum gleichen Thema hier und hier , und mehr oder weniger üblich ist es, zu überprüfen, ob ${0}es sich um den gleichen bash
Skriptnamen handelt
4

Die $1, $2, $3, ..., ${10}, ${11}Variablen werden als Positionsparameter bezeichnet und werden im Abschnitt Bash-Handbuch behandelt3.4.1

3.4.1 Positionsparameter

Ein Positionsparameter ist ein Parameter, der durch eine oder mehrere Ziffern außer der einzelnen Ziffer 0 gekennzeichnet ist. Positionsparameter werden aus den Argumenten der Shell beim Aufrufen zugewiesen und können mit dem Befehl set builtin neu zugewiesen werden. Der Positionsparameter N kann als $ {N} oder als $ N bezeichnet werden, wenn N aus einer einzelnen Ziffer besteht. Positionsparameter dürfen nicht mit Zuweisungsanweisungen belegt werden. Mit den integrierten Funktionen set und shift können Sie sie festlegen und deaktivieren (siehe Shell Builtin Commands). Die Positionsparameter werden vorübergehend ersetzt, wenn eine Shell-Funktion ausgeführt wird (siehe Shell-Funktionen).

Wenn ein Positionsparameter, der aus mehr als einer Ziffer besteht, erweitert wird, muss er in geschweiften Klammern angegeben werden.

In Bezug auf $?und $0werden diese speziellen Parameter im nächsten Abschnitt behandelt3.4.2

3.4.2 Spezielle Parameter

Die Shell behandelt einige Parameter speziell. Diese Parameter dürfen nur referenziert werden; eine Zuordnung zu ihnen ist nicht zulässig.

...

?

($?) Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert.

0

($ 0) Wird auf den Namen der Shell oder des Shell-Skripts erweitert. Dies wird bei der Shell-Initialisierung festgelegt. Wenn Bash mit einer Befehlsdatei aufgerufen wird (siehe Shell-Skripte), wird $ 0 auf den Namen dieser Datei gesetzt. Wenn Bash mit der Option -c gestartet wird (siehe Aufrufen von Bash), wird $ 0 auf das erste Argument nach dem auszuführenden String gesetzt, sofern eines vorhanden ist. Andernfalls wird der Dateiname verwendet, der zum Aufrufen von Bash verwendet wird, wie durch Argument Null angegeben.

Jesse_b
quelle
4

$1, $2... sind Positionsparameter , keine Variablen, geschweige denn Umgebungsvariablen.

In Bourne-Shell wie Terminologie $somethingheißt Parametererweiterung (auch Abdeckungen ${something#pattern}und mehr in einigen Muscheln wie ${array[x]}, ${param:offset}, ${x:|y}und viele weitere Expansion Operatoren).

Es gibt verschiedene Arten von Parametern:

  • Variablen wie $foo,$PATH
  • Positionsparameter ( $1, $2... die Argumente, die Ihr Skript erhalten hat)
  • andere spezielle Parameter wie $0, $-, $#, $*, $@, $$, $!, $?...

Variablennamen in Bourne-ähnlichen Shells müssen mit einem alphabetischen Zeichen beginnen (eines, das vom Gebietsschema erkannt wird oder je nach Shell auf a-zA-Z beschränkt ist) und unterstrichen und von null oder mehr alphanumerischen Zeichen oder Unterstrichen gefolgt werden.

Je nach Shell können Variablen unterschiedliche Typen (Skalar, Array, Hash) oder bestimmte Attribute (schreibgeschützt, exportiert, Kleinbuchstaben ...) haben.

Einige dieser Variablen werden von der Shell erstellt oder haben besondere Bedeutung für die Shell (wie $OPTIND, $IFS, $_...)

Shell-Variablen mit dem Export- Attribut werden automatisch als Umgebungsvariablen in die Befehle exportiert , die die Shell ausführt.

Umgebungsvariable ist ein von Shell-Variablen getrenntes Konzept. Das Exportieren einer Shell-Variablen ist nicht die einzige Möglichkeit, eine Umgebungsvariable an die Ausführung eines Befehls zu übergeben.

VAR=foo
export VAR
printenv VAR

Übergibt eine VARUmgebungsvariable an den printenvBefehl (von dem wir sagen, dass er seinen Inhalt drucken soll). Sie können jedoch auch Folgendes tun:

env VAR=foo printenv VAR

oder:

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

zum Beispiel.

Umgebungsvariablen können einen beliebigen Namen haben (können ein beliebiges Zeichen enthalten, aber =auch leer sein). Es ist keine gute Idee, einer Umgebungsvariablen einen Namen zu geben, der nicht mit dem Namen einer Bourne-ähnlichen Shell-Variablen kompatibel ist, aber es ist möglich:

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

Shells ordnen die Umgebungsvariablen, die sie empfangen , nur für diejenigen Umgebungsvariablen Shell-Variablen zu, deren Name gültige Shell-Variablen sind (und ignorieren in einigen Shells einige spezielle Variablen wie $IFS).

So, während Sie eine 1Umgebungsvariable an einen Befehl übergeben können:

$ env '1=whatever' printenv 1
whatever

Das bedeutet nicht, dass der Aufruf einer Shell mit dieser Umgebungsvariablen den Wert des $1Parameters festlegen würde :

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

Nein, das sind Parameter des Skripts. Zum Beispiel, wenn Sie Ihr Skript wie folgt aufrufen:

mynicescript.sh one two three

Innerhalb des Skripts stehen diese Parameter dann zur Verfügung als

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

und $ 0 ist der Name des Skripts.

Wenn Sie sich also außerhalb des Skripts befinden, sind diese Variablen nicht verfügbar (mit Ausnahme von $ 0, die / bin / bash - die Shell selbst - anzeigt).

Jaroslav Kucera
quelle
"Wenn Sie sich außerhalb des Skripts befinden, sind diese Variablen nicht verfügbar." Was bedeutet "außerhalb des Skripts" , da ich die Werte dieser Variablen in meinem Terminal sehen kann.
user7681202
2
@ user7681202: Welche können Sie in Ihrem Terminal sehen? $0zeigt auf Ihren aktuellen Terminal-Prozess (wahrscheinlich Bash) und $?ist einfach der Exit-Code des letzten Prozesses.
Jesse_b
Ich habe versucht, das gnome-terminalmit Argumenten ( gnome-terminal Hello World) auszuführen . Ich konnte sehen $0, aber ich konnte nicht sehen $1und $2.
user7681202
@ Jesse_b Vielen Dank, ich habe den Inhalt von $ 0 in die Antwort aufgenommen.
Jaroslav Kucera
1
@ user7681202 Das Gnome-Terminal ist keine Shell, sondern ein Terminal-Emulator (wie xterm, konsole usw.). Die Shell läuft im Terminal und kann bash / sh / zsh / tcsh und viele mehr sein. Das Skript ist eine ausführbare Datei mit einem korrekten Header (wie #! / Bin / bash) und Inhalt, der von der in der Maske angegebenen Shell interpretiert werden kann. Normalerweise wird das Suffix .sh verwendet
Jaroslav Kucera