Che cosè “ declare ” in Bash?

Dopo aver letto la risposta di ilkkachu “a questa domanda ho appreso dellesistenza del declare (con argomento -n) shell incorporata.

help declare porta:

Imposta i valori e gli attributi delle variabili.

Dichiara le variabili e assegna loro attributi. Se non viene fornito alcun NOME, visualizza il attributi e valori di tutte le variabili.

-n … rende NAME un riferimento alla variabile denominata dal suo valore

I chiedi una spiegazione generale con un esempio riguardante declare perché “non capisco il man. So cosè una variabile e la sto espandendo, ma mi manca ancora man su declare (attributo variabile?).

Forse ti piacerebbe spiegare questo in base al codice di ilkkachu nella risposta:

#!/bin/bash function read_and_verify { read -p "Please enter value for "$1": " tmp1 read -p "Please repeat the value to verify: " tmp2 if [ "$tmp1" != "$tmp2" ]; then echo "Values unmatched. Please try again."; return 2 else declare -n ref="$1" ref=$tmp1 fi } 

Commenti

Risposta

Nella maggior parte dei casi è sufficiente una dichiarazione implicita in bash

asdf="some text" 

Ma a volte vuoi che il valore di una variabile sia solo intero (quindi nel caso in cui cambiasse in seguito, anche automaticamente, potrebbe essere cambiato solo in un numero intero, in alcuni casi il valore predefinito è zero) e può utilizzare:

declare -i num 

o

declare -i num=15 

A volte vuoi array e poi hai bisogno di declare

declare -a asdf # indexed type 

o

Puoi trovare buoni tutorial sugli array in bash quando navighi in Internet con la stringa di ricerca” bash array tutorial “(senza virgolette), per esempio

linuxconfig.org/how-to-use-arrays-in-bash-script


Penso che questi siano i casi più comuni quando si dichiarano variabili.


Si noti inoltre che

  • in una funzione, declare rende la variabile locale (nella funzione)
  • senza alcun nome, elenca tutte le variabili (nella shell attiva)

    declare 

Infine, ottieni un breve riepilogo delle caratteristiche del comando incorporato della shell declare in bash con il comando

help declare 

Commenti

  • Ciao dallOP! Penso che la tua risposta sia ottima e lho votata contro e ti ringrazio per questa, a mio parere, risposta molto didattica. Ho appena suggerito una modifica minore che a mio modesto parere rende ancora più facile e accessibile la lettura per i nuovi arrivati; segui il suggerimento.
  • Ho appena visto che user:muru ha votato per rifiutare la modifica; tieni presente che io e Muru abbiamo ” scontrato ” più volte nelle diverse sezioni di questo sito web e presumo che il suo rifiuto non sia ‘ t obiettivo ed è effettivamente dannoso in contrasto con le modifiche dirette presentate nella pagina dei suggerimenti di modifica qui: unix.stackexchange.com/review/suggested -edits / 325749
  • @JohnDoea, ho visto che anche un altro utente ha rifiutato le tue modifiche. Penso che alcune delle tue modifiche siano buone, ma alcune cambieranno il messaggio da quello che intendevo, quindi non mi piacerebbero. Posso modificare la risposta per includere quelle che penso siano buone modifiche.
  • grazie; come probabilmente saprai, rifiutare le modifiche è molto più comune in SE che accettarle (anche parzialmente); grazie per avermi fatto sapere che un altro utente ha rifiutato la modifica (non ‘ ho visto prima onestamente) e per aver considerato di inserire almeno alcune delle mie modifiche alla tua risposta; Lo rispetto molto,

Answer

Loutput di help declare è abbastanza conciso. Una spiegazione più chiara può essere trovata in man bash o info bash, questultimo è la fonte di quanto segue.

Innanzitutto, alcune definizioni. Informazioni su variabili e attributi :

Un parametro è unentità che memorizza i valori. … Una variabile è un parametro indicato da un name. Una variabile ha un valore e zero o più attributi . Gli attributi vengono assegnati utilizzando il comando integrato declare

E sul declare integrato :

declare

 declare [-aAfFgilnrtux] [-p] [name[=value] …]  

Dichiara le variabili e assegna loro attributi. Se non vengono forniti nomi, visualizza invece i valori delle variabili.

-n
Dai a ciascuno nomina lattributo nameref , rendendolo un riferimento di nome a unaltra variabile. Quellaltra variabile è definita dal valore di nome . Tutti i riferimenti, le assegnazioni e le modifiche agli attributi a name , ad eccezione di quelli che utilizzano o modificano lattributo -n stesso, vengono eseguiti sulla variabile a cui fa riferimento il valore di name . …

Nota che le variabili riferimento al nome sono disponibili solo in Bash 4.3 o successivo 1 .

Inoltre, per unutile introduzione a declare e agli attributi delle variabili in Bash, vorrei indicarti questo answer a ” Che cosa fanno declare name e declare -g? ” (che si concentra principalmente sullambito delle variabili “, però).


Fondamentalmente 2 , declare name=[value] è equivalente allassegnazione name=[value] che probabilmente conosci. In entrambi i casi, a name viene assegnato il valore null valore se manca value.

Tieni presente che declare name leggermente diverso, invece, non imposta la variabile name 3 :

 $ declare name ## With the -p option, declare is used to display ## attributes and values of variables $ declare -p name declare -- name ## "name" exists ## Parameter expansion can be used to reveal if a variable is set: ## "isunset" is substituted to "name" only if unset $ echo "${name-isunset}" isunset  

Pertanto, la variabile name può essere:

  • dichiarata e unset , dopo declare name;
  • dichiarato e set con null come valore, dopo name= o declare name=;
  • dichiarato , set e con un valore non null dopo name=value o .

Più in generale, declare [options] name=value

  1. crea la variabile name – che è un parametro con un nome, che a sua volta è solo una porzione di memoria che puoi utilizzare per memorizzare le informazioni 4 ;
  2. assegna il valore value ad esso;
  3. opzionalmente imposta gli attributi di name “, che definiscono sia il tipo di valore può memorizzare (non in termini di un tipo , in senso stretto, poiché la lingua di Bash non è tipizzata) e i modi in cui può essere manipolata.

Gli attributi sono probabilmente più facile da spiegare con un esempio: lutilizzo di declare -i name imposterà lattributo ” intero ” di name, lasciando che venga trattato come un numero intero; citando il manuale , la ” valutazione aritmetica verrà eseguita quando alla variabile viene assegnato un valore “:

 ## Let"s compare an ordinary variable with an integer $ declare var $ declare -i int $ var="1+1" $ int="1+1" $ echo "$var" 1+1 ## The literal "1+1" $ echo "$int" 2 ## The result of the evaluation of 1+1  

Alla luce di quanto sopra, ciò che sta accadendo nel codice di ilkkachu è che:

  1. Una variabile denominata ref viene dichiarata, con ” nameref ” set di attributi e il contenuto di $1 (il primo argomento posizionale) è assegnato a it:

     declare -n ref="$1"  

    Lo scopo di una variabile di riferimento del nome come ref serve a contenere il nome di unaltra variabile, che generalmente non sarebbe nota in anticipo, forse perché vogliamo che sia definita dinamicamente (ad es. Perché vogliamo riutilizzare una parte di codice e averla applicato a diverse variabili) e a fornire un modo conveniente per fare riferimento a (e manipolarlo). (Non lunico, però: lindirizzamento è unalternativa; vedere Shell Parameter Expansion ).

  2. Quando il valore della variabile tmp1 è assegnato a ref:

     ref=$tmp1  

    una variabile aggiuntiva, il cui nome è il valore di ref, viene dichiarata implicitamente. Il valore di tmp1 è anche indirettamente assegnato alla variabile dichiarata implicitamente mediante questa assegnazione esplicita a ref .

Nel contesto della domanda collegata , chiamando read_and_verify as

 read_and_verify domain "Prompt text here..."  

dichiarerà la variabile domain e assegnagli il valore di tmp1 (ovvero linput dellutente). È progettato esattamente per riutilizzare il codice che interagisce con lutente e fa leva su una variabile nameref per dichiarare domain e poche altre variabili.

Per dare unocchiata più da vicino alla parte implicita possiamo riprodurre il processo passo dopo passo:

 ## Assign a value to the first positional argument $ set -- "domain" ## Declare the same "tmp1" variable as in your code $ tmp1="value for domain" ## Declare a "ref" variable with the nameref attribute set and ## assign the value "domain" to it $ declare -n ref="$1" ## Note that there is no "domain" variable yet $ declare -p domain bash: declare: domain: not found ## Assign a value to "ref" and, indirectly, to the "domain" variable ## that is implicitly declared $ ref=$tmp1 ## Verify that a variable named "domain" now exists, and that ## its value is that of "tmp1" $ declare -p domain declare -- domain="value for domain" ## Verify that "ref" is actually a reference to "domain" $ domain="new value" $ echo "$domain" new value $ declare -p ref declare -n ref="domain" $ echo "$ref" new value  

1 Riferimento: CHANGES file, sezione ” 3. Nuove funzionalità in Bash “, punto ” w “.
Potrebbe essere pertinente: ad esempio, CentOS Linux 7.6 (attualmente il ultima versione) viene fornito con Bash 4.2 .

2 Come al solito con incorporati di shell, una spiegazione e concisa esauriente è sfuggente poiché eseguono varie azioni, forse eterogenee. Mi concentrerò solo sulla dichiarazione, lassegnazione e limpostazione degli attributi e prenderò in considerazione lelenco, lambito e la rimozione degli attributi come al di fuori dellambito di questa risposta.

3 Questo comportamento di declare -p è stato introdotto in Bash 4.4. Riferimento: file CHANGES , sezione ” 3. Nuove funzionalità in Bash “, punto ” f “.
As G-Man sottolineato nei commenti, in Bash 4.3 declare name; declare -p name restituisce un errore. Puoi comunque verificare che name esista con declare -p | grep "declare -- name".

4 FullBashGuide, Parametri su mywiki.wooledge.org

Commenti

  • (1) Non riesco a riprodurre i risultati mostrati nel tuo primo blocco di codice: declare name seguito da declare -p name restituisce “bash: declare: name: not found”. (Anche se declare -p | grep na restituisce declare -- name.) (2) Credo che sia un po fuorviante presentare echo "${name-isunset}" nel contesto di declare name, in quanto tratta una variabile non dichiarata (cioè non definita) allo stesso modo di una variabile dichiarata ma unset variabile. (3) Potresti menzionare che i nomi sono disponibili solo nella versione bash 4.3 e successive.
  • @ G-Man Grazie per i tuoi commenti! ‘ li affronterò il prima possibile e ‘ aggiornerò la mia risposta dove appropriato. Per quanto riguarda (1), il mio GNU bash, version 5.0.7(1)-release (x86_64-pc-linux-gnu) su Arch Linux fornisce ancora i risultati che ho mostrato. Forse quel comportamento è stato introdotto solo di recente, ‘ lo indagherò.
  • Sì; ‘ utilizzo solo la versione 4.3.
  • @ G-Man Answer aggiornata con note su (1) e (3). Informazioni su (2): volevo illustrare che declare x non ‘ t imposta x, mentre declare x= sì. Non sono riuscito ‘ a trovare alcun riferimento a sostegno dellasserzione che declare -- x (come output di declare -p x) significa ” non impostato “, mentre declare -- x="" significa ” set “; quindi ho introdotto lespansione ${parameter-word}, anche se non può discriminare tra ” unset ” e ” non ‘ esiste “, come hai indicato. Tuttavia, ‘ non sono sicuro di come chiarirlo nella mia risposta (senza distrarre il lettore dal punto).

Rispondi

Provo a spiegarlo, ma perdonami se non seguo lesempio che hai fornito. Cercherò piuttosto di guidarti lungo il mio approccio diverso.

Dici che hai già compreso concetti come “variabili” e “espanderle”, ecc., Quindi mi limiterò a scorrere alcuni conoscenze di base che altrimenti richiederebbero una maggiore attenzione.

Quindi inizierò col dire che, al massimo basic , il comando declare è solo un modo per dire a Bash che hai bisogno di un valore variabile (cioè un valore che potrebbe cambiare durante lesecuzione dello script) e che lo farai fai riferimento a quel valore utilizzando un nome specifico, precisamente il nome che indichi accanto al comando declare stesso.

Ovvero:

 declare foo="bar"  

dice a Bash che tu desidera che la variabile denominata foo abbia il valore bar.

Ma … aspetta un minuto .. possiamo fallo senza utilizzare declare, non possiamo. Come in:

 foo="bar"  

Molto vero.

Bene , così accade che la semplice assegnazione di cui sopra sia in realtà un implicito per .. infatti .. dichiarare una variabile.

( Accade anche che quanto sopra sia uno di alcuni modi per cambia il valore della variabile denominata foo; in effetti è proprio la più diretta, modo conciso, evidente, diretto .. ma non è lunico .. .. tornerò su questo più tardi .. ).

Ma poi, se è così ben possibile dichiarare un “nome che taggerà i valori delle variabili” (solo “variabile” dora in poi, per brevità) senza utilizzare affatto declare, perché mai dovresti volerlo usare questo pomposo comando “dichiara”?

La risposta sta nel fatto che quanto sopra implici t modo per dichiarare una variabile (foo="bar"), .. implicitamente .. fa sì che Bash consideri quella variabile del tipo più comunemente usato nello scenario di utilizzo tipico di una shell .

Questo tipo è il tipo stringa, cioè una sequenza di caratteri senza un significato particolare. Quindi una stringa è ciò che ottieni quando usi la dichiarazione implicita.

Ma tu, come programmatore, a volte devi piuttosto considerare una variabile come, ad esempio, un numero .. su cui devi fare aritmetica operazioni .. e utilizzando una dichiarazione implicita come foo=5+6 non Bash assegna il valore 11 a foo come potresti aspettarti. Assegnerà piuttosto a foo la sequenza dei tre caratteri 5 + 6.

Quindi … hai bisogno di un modo per dire a Bash che vuoi che foo sia considerato un numero, non un stringa .. ed è per questo che viene utile un declare.

Dì solo:

 declare -i foo=5+6 # <<- note the "-i" option: it means "integer"  

e Bash farà felicemente i conti per te e assegnerà il valore numerico 11 alla variabile foo.

Ovvero: dicendo declare -i foo dai alla variabile foo il attributo di essere un numero intero.

La dichiarazione di numeri (precisamente interi, perché Bash continua a non capire i decimali, i punti mobili e tutto il resto) può essere la prima ragione per usare declare, ma non è lunico motivo. Come hai già capito, ci sono alcuni altri attributi che puoi dare alle variabili. Ad esempio, puoi fare in modo che Bash crei sempre il valore di una variabile in maiuscolo, non importa cosa: se dici declare -u foo, da quel momento in poi quando dici foo=bar Bash effettivamente assegna la stringa BAR alla variabile foo.

Per fornire uno qualsiasi di questi attributi a una variabile, devi utilizzare il comando declare, non cè altra scelta.


Ora, un altro dei attributi che puoi fornire tramite declare è il famigerato “name-ref”, lattributo -n. ( E ora riprenderò il concetto che ho sospeso prima ).

Lattributo name-ref, fondamentalmente, consente ai programmatori Bash di cambiare il valore in un altro modo di una variabile. Fornisce più precisamente un modo indiretto per farlo.

Ecco come funziona:

Tu declare una variabile con lattributo -n ed è molto consigliato (anche se non strettamente richiesto, ma rende le cose più semplici) di dare anche un valore a questo molto variabile sulla stessa declare. In questo modo:

 declare -n baz="foo"  

Questo dice a Bash che, da allora ogni volta che utilizzerai o modificherai il valore della variabile denominata baz, verrà effettivamente utilizzato o modificato il valore della variabile denominata foo.

Il che significa che, da quel momento in poi, yo puoi dire qualcosa come baz=10+3 per fare in modo che foo ottenga il valore 13.Supponendo ovviamente che foo sia stato precedentemente dichiarato come intero (declare -i) come abbiamo fatto solo un minuto fa, altrimenti otterrà la sequenza dei quattro caratteri 1 0 + 3.

Inoltre: se modifichi il valore di foo direttamente, come in foo=15, vedrai 15 anche dicendo echo “${baz}”. Questo perché la variabile baz dichiarata come name-ref di foo riflette sempre foo valore.

Il comando declare -n è detto “riferimento nome” perché rende la variabile baz fare riferimento al nome di unaltra variabile. Infatti abbiamo dichiarato che baz ha valore “foo” che, a causa dellopzione -n, è gestito da Bash come nome di unaltra variabile.

Ora, perché sulla Terra vorresti mai farlo?

Beh … vale la pena dire che questa è una funzionalità per esigenze piuttosto avanzate.

In effetti così avanzata che quando un programmatore deve affrontare un problema che richiederebbe davvero un nome-ref, è anche è probabile che tale problema debba essere affrontato utilizzando un linguaggio di programmazione appropriato invece di Bash.

Una di quelle esigenze avanzate è, ad esempio, quando tu, come programmatore, non puoi saperlo durante lo sviluppo quale variabile dovrai usare in un punto specifico di uno script, ma sarà completamente nota dinamicamente in fase di esecuzione. E dato che non cè modo per nessun programmatore di intervenire in fase di esecuzione, lunica opzione è quella di prevedere in anticipo tale situazione nello script, e un “nome-riferimento” può essere lunico possibile modo. Come caso duso ampiamente noto di questa esigenza avanzata, pensa ad esempio ai plug-in. Il programmatore di un programma “plug-in in grado” deve predisporre preventivamente i plug-in futuri (e possibilmente di terze parti). Quindi il programmatore dovrà usare servizi come un nome-ref in Bash.

Unaltra esigenza avanzata è quando devi gestire enormi quantità di dati nella RAM e anche tu devi passare quei dati alle funzioni del tuo script che anche devono modificare quei dati lungo il percorso. In tal caso potresti certamente copiare quei dati da una funzione a unaltra (come fa Bash quando fai dest_var="${src_var}" o quando richiami funzioni come in myfunc "${src_var}"), ma trattandosi di una quantità enorme di dati, sarebbe un enorme spreco di RAM e per unoperazione molto inefficiente. Quindi la soluzione, se si verificano tali situazioni, non è utilizzare una copia dei dati, ma un riferimento a tali dati. In Bash, un nome-rif. Questo caso duso è davvero la norma in qualsiasi linguaggio di programmazione moderno, ma è abbastanza eccezionale quando si tratta di Bash, perché Bash è progettato principalmente per script brevi e semplici che trattano principalmente file e comandi esterni e quindi gli script Bash raramente devono passare enormi quantità di dati tra le funzioni. E quando le funzioni di uno script hanno bisogno di condividere alcuni dati (accedervi e modificarli), di solito si ottiene semplicemente usando una variabile globale, che è abbastanza la norma negli script Bash tanto quanto molto deprecato nei linguaggi di programmazione appropriati.

Quindi, potrebbe esserci un caso duso notevole per i nomi-ref in Bash, e (forse ironicamente) è associato a quando usi ancora altri tipi di variabili:

  1. variabili dichiarate come “array indicizzati” (declare -a)
  2. variabili dichiarate come “array associativi” (declare -A).

Si tratta di un tipo di variabili che possono essere più facilmente (oltre che più efficientemente) trasmesse alle funzioni utilizzando name-ref invece che con la normale copia, anche quando non trasportano enormi quantità di dati.

Se tutti questi esempi suonano strani e ancora incomprensibili, è solo perché name-ref sono davvero un argomento avanzato e una rara necessità per lo scenario di utilizzo tipico di B ash.

Potrei parlarvi di occasioni in cui io per primo ho trovato luso di nomi-ref in Bash, ma finora sono stati principalmente per esigenze piuttosto “esoteriche” e complicate, e io sono paura che se li descrivessi ti complicherei solo le cose a questo punto del tuo apprendimento. Solo per citare il meno complesso (e forse non esoterico): restituire valori dalle funzioni. Bash in realtà non supporta questa funzionalità, quindi ho ottenuto lo stesso utilizzando name-refs. Questo, per inciso, è esattamente ciò che fa il tuo codice di esempio.


Oltre a questo, un piccolo consiglio personale, che in realtà sarebbe più adatto per un commento ma non sono stato in grado di condensarlo abbastanza per adattarsi ai limiti dei commenti di StackExchange.

Penso che il massimo che dovresti fare al momento è solo sperimentare con i nomi-ref usando i semplici esempi che ho mostrato e forse con il codice di esempio che hai fornito, ignorando per il momento la parte “perché mai” e concentrandosi solo sulla parte “come funziona”. Sperimentando un po , la parte “come” potrebbe affondare meglio nella tua mente, in modo che la parte “perché” ti verrà chiara a tempo debito quando (o se) avrai un vero problema pratico per il quale un nome- ref sarebbe davvero utile.

Commenti

  • LL3 Mi piace molto la tua risposta e ho espresso il pollice in su: finalmente capisco cosa fa declare – – non dichiarare variabili ma dichiararne gli attributi; eppure purtroppo ho perso il conto quando hai iniziato a spiegare cosè un nome-rif. Non ho idea di cosa faccia e perché usarlo.
  • Cioè – perché si dovrebbe dare questo attributo
  • @JohnDoea, vedo. Forse potresti lasciare questo argomento per il momento. Probabilmente è troppo presto per afferrare questo concetto al momento del tuo apprendimento. Comunque ho ampliato la mia risposta con altri esempi e anche una piccola aggiunta personale su come penso che dovresti procedere. Ho anche inserito delle linee orizzontali per delimitare la spiegazione generale di declare dalla spiegazione specifica di -n dalladdendum personale. Ho anche riformulato un po qua e là ma niente di sostanziale, quindi penso che potresti rileggere la parte -n più la piccola aggiunta.
  • Grazie, penso che ‘ vada bene per me imparare su questo in questo momento, ho solo bisogno di una spiegazione che posso capire e dovrei rileggere lintera risposta oggi, io ‘ m qui intorno.
  • Citi foo="bar" e declare foo="bar" . Potresti menzionare (anche solo in una nota a piè di pagina) che, in una funzione di shell, declare foo="bar" crea una variabile locale e foo="bar" crea una variabile globale.

Risposta

In generale, declare in la bash shell imposta (o rimuove o visualizza) attributi sulle variabili. Un attributo è una sorta di annotazione che dice “questoèun riferimento al nome”, o “questoèun array associativo”, o “questa variabile dovrebbe essere sempre valutata come un intero”, o “questa variabileèdi sola lettura e non può essere reimpostata “, o” questa variabile viene esportata (una variabile dambiente) “ecc.

Il typeset integrato è un sinonimo di declare in bash, poiché typeset viene utilizzato in altre shell (ksh, dove ha avuto origine, e zsh, ad esempio) per impostare gli attributi delle variabili.


Esaminando più da vicino lesempio di riferimento del nome in la domanda:

La funzione shell che mostri, con un po di codice in più che la utilizza:

#!/bin/bash function read_and_verify { read -p "Please enter value for "$1": " tmp1 read -p "Please repeat the value to verify: " tmp2 if [ "$tmp1" != "$tmp2" ]; then echo "Values unmatched. Please try again."; return 2 else declare -n ref="$1" ref=$tmp1 fi } read_and_verify foo printf "The variable "foo" has the value "%s"\n" "$foo" 

Esecuzione di questo:

 $ bash script.sh Please enter value for "foo": hello Please repeat the value to verify: hello? Values unmatched. Please try again. The variable "foo" has the value "" 

Ciò mostra che la variabile foo non è impostata su alcun valore quando lutente inserisce due diversi stringhe.

Questo mostra che la variabile foo viene impostata sulla stringa che lutente ha inserito quando ha inserito la stessa stringa due volte .

Il modo in cui $foo ottiene il valore hello nella parte principale dello script è con le seguenti righe nella funzione shell:

declare -n ref="$1" ref=$tmp1 

dove $tmp1 è la stringa hello immesso dallutente e $1 è la stringa foo passata alla riga di comando della funzione dalla parte principale del script.

Si noti che la variabile ref viene dichiarata con declare -n come variabile di riferimento del nome e che il valore foo viene fornito come valore in quella dichiarazione. Ciò significa che da quel momento in poi, fino a quando la variabile non esce dallambito, qualsiasi utilizzo della variabile ref sarà uguale alluso di foo. La variabile ref è una variabile di riferimento del nome che fa riferimento a foo a questo punto.

Ciò ha come conseguenza che assegnare un valore a ref, come si fa nella riga successiva alla dichiarazione, assegnerà il valore a foo.

Il valore hello è quindi disponibile in $foo nella parte principale dello script.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *