Sono variabili come $ 0 e $ 1 variabili shell / ambiente?


17

Ci sono variabili nella shell come $0, $1, $2, $?, etc.

Ho provato a stampare la shell e le variabili d'ambiente usando il seguente comando:

set

Ma queste variabili non erano nell'elenco.

Quindi sostanzialmente queste variabili non sono considerate variabili shell / ambiente, giusto? (anche se per produrli, devi precederli con un $, come fai con le variabili shell / ambiente)


3
I parametri posizionali non sono variabili. Non è possibile export 3trasformarsi $3in una variabile di ambiente. Non puoi unset 3; e non puoi assegnare $3un nuovo valore usando 3=val.
Kaz,

Risposte:


25

Le variabili sono una delle tre distinte varietà di parametri nella shell.

  1. Una variabile è un parametro il cui nome è un identificatore di shell valido; inizia con _o una lettera, seguita da zero o più lettere, numeri o _.
  2. I posizionali parametri sono i parametri numerati $1, $2, ...
  3. Tutti i parametri speciali hanno nomi a carattere singolo e, a parte $0, sono tutti i vari caratteri di punteggiatura.

set visualizza solo le variabili della shell.

Un sottoinsieme delle variabili della shell sono le variabili di ambiente, i cui valori vengono ereditati dall'ambiente all'avvio della shell oppure creati impostando l' exportattributo su un nome valido.


1
Nota che setvisualizza tutti i parametri in zsh(non $ 1, $ 2 ... ma $ *, $ @) e le funzioni in bash e bosh. Alcune shell come ksh93 e le versioni precedenti dell'output dash non inviano mappature alle variabili shell. ( env 1=foo ksh -c setstampa 1=foo)
Stéphane Chazelas,

11

Variabili d'ambiente vs parametri posizionali

Prima di iniziare a discutere il $INTEGERtipo di variabili, dobbiamo capire cosa sono realmente e in che modo differiscono dalle variabili di ambiente. Variabili come i $INTEGERcosiddetti parametri posizionali. Questo è descritto nello standard POSIX (Portable Operating System Interface), sezione 2.1 (enfasi sulla miniera):

  1. La shell esegue una funzione (vedere Comando definizione funzione), integrata (vedi Utilità integrate speciali), file eseguibile o script, fornendo i nomi degli argomenti come parametri posizionali numerati da 1 a n e il nome del comando (o nel caso di una funzione all'interno di uno script, il nome dello script) come parametro posizionale numerato 0 (vedere Ricerca ed esecuzione dei comandi).

Al contrario, variabili come $HOMEe $PATHsono variabili di ambiente. La loro definizione è descritta nella sezione 8 della norma :

Le variabili d'ambiente definite in questo capitolo influiscono sul funzionamento di più utility, funzioni e applicazioni. Esistono altre variabili d'ambiente che interessano solo utilità specifiche. Le variabili d'ambiente che si applicano a una singola utility sono definite come parte della descrizione dell'utility.

Nota la loro descrizione. I parametri posizionali devono apparire davanti a un comando, ad es command positional_arg_1 positional_arg_2.... Sono pensati per essere forniti dall'utente per dire al comando cosa fare specificamente. Quando lo fai echo 'Hello' 'World', stamperà le stringhe Helloe World, perché questi sono parametri posizionali su echo- le cose su cui vuoi echooperare. Ed echoè costruito in modo tale da comprendere i parametri posizionali come stringhe da stampare (a meno che non siano uno dei flag opzionali come -n). Se lo fai con un comando diverso, potrebbe non capire cosa HelloeWorldè perché forse si aspetta un numero. Si noti che i parametri posizionali non sono "ereditati": un processo figlio non è a conoscenza dei parametri posizionali del genitore se non è passato esplicitamente al processo figlio. Spesso vedi che i parametri posizionali vengono passati con gli script wrapper - quelli che forse controllano l'istanza esistente di un comando o aggiungono ulteriori parametri posizionali al comando reale che verrà chiamato.

Al contrario, le variabili di ambiente sono destinate a influenzare più programmi. Sono variabili d' ambiente , perché sono impostate al di fuori del programma stesso (più su questo di seguito). Alcune variabili di ambiente come HOMEo PATHhanno un formato specifico, un significato specifico e significheranno lo stesso per ciascun programma. HOMELa variabile avrà lo stesso significato sia per l'utilità esterna che per /usr/bin/findla shell (e, di conseguenza, per uno script): è la directory home del nome utente in cui viene eseguito il processo. Si noti che le variabili ambientali possono essere utilizzate per tenere conto del comportamento specifico del comando, ad esempioUIDla variabile di ambiente può essere utilizzata per verificare se lo script viene eseguito con privilegi di root o meno e ramificare di conseguenza azioni specifiche. Le variabili di ambiente sono ereditabili: i processi figlio ottengono una copia dell'ambiente del genitore. Vedi anche Se i processi ereditano l'ambiente del genitore, perché dobbiamo esportare?

In breve, la distinzione principale è che le variabili di ambiente sono impostate al di fuori del comando e non sono pensate per essere variate (di solito), mentre i parametri posizionali sono cose che devono essere elaborate dal comando e cambiano.


Non solo concetti di shell

Quello che ho notato dai commenti è che stai mescolando terminali e shell, e ti consiglierei davvero di leggere su terminali reali che una volta erano dispositivi fisici. Al giorno d'oggi, il "terminale" a cui ci riferiamo in genere, quella finestra con sfondo nero e testo verde è in realtà un software, un processo. Terminal è un programma che esegue una shell, mentre shell è anche un programma ma quello che legge ciò che si digita per eseguire (ovvero, se si tratta di shell interattiva; le shell non interattive sono script e sh -c 'echo foo'tipi di invocazioni). Maggiori informazioni sulle conchiglie qui .

Questa è una distinzione importante, ma anche importante riconoscere che il terminale è un programma e quindi aderisce alle stesse regole di ambiente e parametri posizionali. Il tuo gnome-terminalquando avviato guarderà la tua SHELLvariabile di ambiente e genererà la shell predefinita appropriata per te, a meno che non specifichi qualche altro comando con -e. Diciamo che ho cambiato la mia shell predefinita in ksh - gnome-terminal verrà quindi generato kshinvece di bash. Questo è anche un esempio di come l'ambiente viene utilizzato dai programmi. Se io dico in modo esplicito gnome-terminalcon -eeseguire shell specifica - lo farà, ma non sarà permanente. Al contrario, l'ambiente dovrebbe essere per lo più inalterato (ne parleremo più avanti).

Come puoi vedere, le variabili di ambiente e di posizione sono entrambe proprietà di un processo / comando, non solo di shell. Quando si tratta di script di shell, seguono anche il modello impostato dal linguaggio di programmazione C. Prendiamo ad esempio la mainfunzione C che in genere sembra

int main(int argc, char **argv)

, dove argcc'è il numero di argomenti della riga di comando ed argvè effettivamente un array di parametri della riga di comando, e poi c'è la environfunzione (su Linux che è man -e 7 environ) di accedere a cose come il percorso della directory home dell'utente, l'elenco delle directory in PATHcui possiamo cercare eseguibili, ecc. Anche gli script di shell sono modellati in modo simile. Nella terminologia shell, abbiamo parametri posizionali $1, $2e così via, mentre $#certo numero di parametri posizionali. Che dire $0? Questo è il nome dell'eseguibile stesso, che è di nuovo modellato dal linguaggio di programmazione C - argv[0]sarebbe il nome del tuo "eseguibile". E questo è abbastanza vero per la maggior parte dei linguaggi di programmazione e scripting .

Shell interattive vs non interattive

Una delle cose che ho già accennato è la distinzione tra shell interattive e non interattive . Il prompt in cui si digitano i comandi: interattivo, interagisce con l'utente. Al contrario, quando si dispone di uno script di shell o si esegue bash -c''che non è interattivo.

Ed è qui che la distinzione diventa importante. La shell che si esegue già è un processo, che è stato generato con parametri posizionali (per la bashshell di login è uno "... il cui primo carattere dell'argomento zero è un -, o uno è iniziato con l'opzione --login." ( Riferimento ) )

Al contrario, gli script e le shell avviati con l' -copzione possono trarre vantaggio $1e $2argomenti. Per esempio,

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

Si noti che ho usato anche shlì, perché una piccola stranezza di -copzione è quella di prendere il primo parametro posizionale e assegnarlo, a $0differenza del solito essere un nome del programma.

Un'altra cosa che è importante notare è che i parametri posizionali sono quelli che io chiamo "framable". Notate come, inizialmente abbiamo lanciato bashi propri parametri posizionali, ma quei parametri posizionali sono diventati parametri a echoe stat. E ogni programma lo capisce a modo suo. Se dessimo statuna stringa Hello Worlde non ci fosse alcun file Hello World, si produrrebbe un errore; bashla tratta come una semplice stringa ma si stataspetta che quella stringa sia un nome file esistente. Al contrario, tutti i programmi concorderebbero sul fatto che la variabile d'ambiente HOMEè una directory (a meno che il programmatore non la abbia codificata in modo irragionevole).


Possiamo pasticciare con variabili d'ambiente e parametri posizionali?

Tecnicamente, possiamo fare confusione con entrambi, ma non dovremmo fare confusione con le variabili di ambiente, mentre spesso dobbiamo fornire parametri posizionali. Possiamo eseguire comandi in shell con anteporre una variabile, ad esempio:

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

Possiamo anche posizionare le variabili nell'ambiente semplicemente usando export variable=valuedalla shell o dallo script. Oppure possiamo eseguire un comando con un ambiente completamente vuoto con env -c command arg1 arg2. Tuttavia, in genere non è consigliabile scherzare con l'ambiente, in particolare utilizzando variabili maiuscole o sovrascrivendo variabili di ambiente già esistenti. Si noti che è raccomandato sebbene non sia uno standard.

Per i parametri posizionali, il modo di impostarli è ovvio, basta anteporli al comando, ma ci sono anche modi per impostarli in altro modo , oltre a cambiare l'elenco di tali parametri tramite shiftcomando.

In conclusione, lo scopo di questi due è diverso ed esistono per un motivo. Spero che la gente abbia avuto un'idea di questa risposta, ed è stato divertente leggerlo come è stato per me scrivere questa risposta.


Nota sul comando set

Il setcomando, secondo il manuale, si comporta in questo modo (dal manuale di bash, enfasi aggiunta):

Senza opzioni, il nome e il valore di ciascuna variabile di shell vengono visualizzati in un formato che può essere riutilizzato come input per l'impostazione o il ripristino delle variabili attualmente impostate.

In altre parole, setesamina le variabili specifiche della shell, alcune delle quali si trovano nell'ambiente, ad esempio HOME. Al contrario i comandi gradiscono enve printenvosservano la variabile d'ambiente effettiva con cui viene eseguito un comando. Si veda anche questo .


"Nella shell interattiva, non puoi fare riferimento a $ 1, $ 2 e così via." Esiste un riferimento diretto per questo? Mi sono imbattuto in strani casi in cui questi sono impostati in un ambiente di shell interattivo e non sono sicuro che questo sia considerato "non standard" o meno.
user5359531

@ user5359531 Onestamente, non sono sicuro da dove l'ho preso. Probabilmente perché quando ho pubblicato la risposta nel 2017, probabilmente ho fatto riferimento che non puoi fare qualcosa del genere, 1="foo"ma in seguito ho scoperto che per definizione POSIX una "parola" (che è un nome di un oggetto come una variabile o una funzione) non può iniziare con una cifra (vedi una domanda che ho pubblicato sull'argomento ). I parametri posizionali apparentemente fanno eccezione a questa regola.
Sergiy Kolodyazhnyy il

@ user5359531 Ho rimosso la parte della risposta, poiché non è particolarmente accurata. È possibile fare riferimento $1, $2e così via nella shell interattiva, e in effetti è fatto con setcomando, spesso per aggirare la /bin/shlimitazione del non avere array. Grazie per averlo segnalato alla mia attenzione. Ho anche intenzione di modificare la risposta nei prossimi giorni, dal momento che ha bisogno di un po 'di smalto in più e di un aggiornamento.
Sergiy Kolodyazhnyy il

Grazie per il chiarimento. Il problema che sto riscontrando è che conda, quando corri source conda/bin/activate, controlla se $1, $2ecc., Sono impostati, al fine di determinare se è stato eseguito come uno script con argomenti o meno. Questo finisce per rompersi sui sistemi che hanno quelli impostati nell'ambiente interattivo per qualche motivo. Spero di capire se questo comportamento non standard è un difetto nel sistema per impostare queste variabili nell'ambiente interattivo o sul programma per usarle per determinare se è stato eseguito come script.
user5359531

@ user5359531 Ti suggerirei di presentare una segnalazione di bug agli condasviluppatori oa chiunque sia l'autore originale di tale script, poiché il controllo dei ${N}parametri è sicuramente la strada sbagliata. Ci sono domande sullo stesso argomento qui e qui , e un modo più o meno portatile è verificare se ${0}è lo stesso del nome dello script, mentre in bashrealtà ha una variabile d'ambiente a tale scopo
Sergiy Kolodyazhnyy

4

Le $1, $2, $3, ..., ${10}, ${11}variabili sono chiamate parametri posizionali e sono trattate nella sezione manuale di bash3.4.1

3.4.1 Parametri posizionali

Un parametro posizionale è un parametro indicato da una o più cifre, diverso dalla singola cifra 0. I parametri posizionali vengono assegnati dagli argomenti della shell quando viene invocato e possono essere riassegnati usando il comando set incorporato. Il parametro posizionale N può essere indicato come $ {N} o come $ N quando N è costituito da una singola cifra. I parametri posizionali non possono essere assegnati con istruzioni di assegnazione. Gli insiemi set e shift vengono usati per impostarli e disinserirli (vedere Comandi incorporati nella shell). I parametri posizionali vengono temporaneamente sostituiti quando viene eseguita una funzione di shell (vedere Funzioni di shell).

Quando un parametro posizionale costituito da più di una singola cifra viene espanso, deve essere racchiuso tra parentesi graffe.

Per quanto riguarda $?e $0, questi parametri speciali sono trattati nella sezione successiva3.4.2

3.4.2 Parametri speciali

La shell tratta diversi parametri appositamente. Questi parametri possono solo essere referenziati; l'assegnazione a loro non è consentita.

...

?

($?) Si espande allo stato di uscita dell'ultima pipeline in primo piano eseguita.

0

($ 0) Si espande nel nome della shell o dello script della shell. Questo è impostato all'inizializzazione della shell. Se Bash viene invocato con un file di comandi (consultare Script di shell), $ 0 viene impostato sul nome di quel file. Se Bash viene avviato con l'opzione -c (consultare Invocare Bash), $ 0 viene impostato sul primo argomento dopo l'esecuzione della stringa, se presente. In caso contrario, viene impostato sul nome file utilizzato per richiamare Bash, come indicato dall'argomento zero.


4

$1, $2... sono parametri posizionali , non sono variabili, figuriamoci variabili d'ambiente.

In Bourne-shell come terminologia, $somethingè chiamato parametro di espansione (anche copertine ${something#pattern}e più in alcuni gusci come ${array[x]}, ${param:offset}, ${x:|y}e molti operatori espansione più).

Esistono diversi tipi di parametri:

  • variabili come $foo,$PATH
  • parametri posizionali ( $1, $2... gli argomenti ricevuti dallo script)
  • altri parametri speciali come $0, $-, $#, $*, $@, $$, $!, $?...

i nomi delle variabili in Bourne come shell devono iniziare con un carattere alfabetico (qualsiasi riconosciuto dalla locale o limitato a a-zA-Z a seconda della shell) e carattere di sottolineatura e seguito da zero o più caratteri alfanumerici o caratteri di sottolineatura.

A seconda della shell, le variabili possono avere tipi diversi (scalare, array, hash) o dati determinati attributi (sola lettura, esportati, minuscoli ...).

Alcune di queste variabili sono create dalla shell o hanno significato speciale per la shell (come $OPTIND, $IFS, $_...)

Le variabili di shell che hanno l' attributo export vengono automaticamente esportate come variabili di ambiente nei comandi eseguiti dalla shell.

La variabile d'ambiente è un concetto separato dalle variabili della shell. L'esportazione di una variabile di shell non è l'unico modo per passare una variabile di ambiente all'esecuzione di un comando.

VAR=foo
export VAR
printenv VAR

passerà una VARvariabile d'ambiente al printenvcomando (che stiamo dicendo di stampare il suo contenuto), ma puoi anche fare:

env VAR=foo printenv VAR

o:

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

per esempio.

Le variabili di ambiente possono avere qualsiasi nome (possono contenere qualsiasi carattere ma =possono anche essere vuote). Non è una buona idea assegnare un nome non compatibile con quello di una variabile shell tipo Bourne a una variabile d'ambiente, ma è possibile:

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

Le shell mapperanno le variabili di ambiente che ricevono sulle variabili di shell solo per quelle variabili di ambiente il cui nome sono variabili di shell valide (e in alcune shell ignorano quelle speciali come $IFS).

Quindi, mentre saresti in grado di passare una 1variabile d'ambiente a un comando:

$ env '1=whatever' printenv 1
whatever

Ciò non significa che chiamare una shell con quella variabile d'ambiente imposterà il valore del $1parametro:

$ env '1=whatever' sh -c 'echo "$1"' script-name foo bar
foo

3

No, questi sono parametri dello script. Ad esempio se chiami il tuo script come:

mynicescript.sh one two three

quindi all'interno dello script, ci saranno questi parametri disponibili come

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

e $ 0 è il nome dello script stesso.

Quindi quando sei fuori dallo script, queste variabili non sono disponibili (tranne $ 0, che mostra / bin / bash - la shell stessa).


"Quindi quando sei fuori dallo script, queste variabili non sono disponibili" Cosa intendi con "fuori dallo script" , perché posso vedere i valori di queste variabili nel mio terminale.
user7681202,

2
@ user7681202: quali vedi nel tuo terminale? $0indicherà l'attuale processo terminale (probabilmente bash) ed $?è semplicemente il codice di uscita dell'ultimo processo.
Jesse_b,

Ho provato a eseguire il gnome-terminalcon argomenti ( gnome-terminal Hello World). Ho potuto vedere $0, ma non ho potuto vedere $1e $2.
user7681202,

@Jesse_b Grazie, ho aggiunto il contenuto di $ 0 alla risposta.
Jaroslav Kucera,

1
@ user7681202 Il terminale gnome non è shell, è un emulatore di terminale (come xterm, konsole ecc.). La shell gira all'interno del terminale e può essere bash / sh / zsh / tcsh e molti altri. Lo script è un file eseguibile con un'intestazione corretta (come #! / Bin / bash) e contenuto interpretabile dalla shell specificata nella maschera. Di solito usa il suffisso .sh
Jaroslav Kucera il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.