Vad är “ förklara ” i Bash?

Efter att ha läst ilkkachus svar på den här frågan fick jag veta om existensen av declare (med argument -n) skal inbyggt.

help declare ger:

Ställ in variabelvärden och attribut.

Förklara variabler och ge dem attribut. Om inga NAMN anges, visa attribut och värden för alla variabler.

-n … gör NAME till en referens till variabeln som namnges av dess värde

I be om en allmän förklaring med ett exempel angående declare eftersom jag inte förstår man. Jag vet vad som är en variabel och expanderar den men jag saknar fortfarande mandeclare (variabelt attribut?).

Kanske vill du förklara detta baserat på koden av ilkkachu i svaret:

#!/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 } 

Kommentarer

Svar

I de flesta fall räcker det med en implicit deklaration i bash

asdf="some text" 

Men ibland vill du att ett variabelvärde bara ska vara heltal (så om det senare skulle ändras, även automatiskt, kunde det bara ändras till ett heltal, är i vissa fall som standard noll) och kan använda:

declare -i num 

eller

declare -i num=15 

Ibland vill du ha matriser och då behöver du declare

declare -a asdf # indexed type 

eller

Du kan hitta bra handledning om matriser i bash när du surfar på internet med söksträngen” bash array tutorial ”(utan citat), för exempel

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


Jag tror att det här är de vanligaste fallen när du deklarerar variabler.


Observera också att

  • i en funktion, declare gör variabeln lokal (i funktionen)
  • utan något namn, den visar alla variabler (i det aktiva skalet)

    declare 

Slutligen får du en kort sammanfattning av funktionerna i det inbyggda skalkommandot declare i bash med kommandot

help declare 

Kommentarer

  • Hej från OP! Jag tycker att ditt svar är bra och jag har röstat det och tackar för detta, enligt min mening, ett mycket didaktiskt svar. Jag har just föreslagit en mindre redigering som enligt min ödmjuka åsikt gör det ännu lättare och tillgängligare för nykomlingar att läsa; snälla gå igenom det förslaget.
  • Jag såg precis att user:muru röstade för att avvisa redigeringen; snälla vet att jag och muru har ” kolliderat med ” flera gånger i de olika avsnitten på denna webbplats och jag antar att hans avslag inte är ’ t-mål och är faktiskt skadligt i motsats till de direkta ändringarna som presenteras på sidan för redigeringsförslag här: unix.stackexchange.com/review/suggested -edits / 325749
  • @JohnDoea, jag såg att även en annan användare har avvisat dina ändringar. Jag tycker att några av dina ändringar är bra, men vissa av dem kommer att ändra meddelandet från vad jag tänkte, så jag skulle inte vilja ha dem. Jag kan redigera svaret så att det inkluderar bra redigeringar.
  • tack; som du säkert vet är att avvisa ändringar mycket vanligare i SE än att acceptera dem (även delvis). tack för att du meddelade att en annan användare avvisade redigeringen (jag såg inte ’ innan) och för att du övervägde att infoga åtminstone några av mina ändringar i ditt svar; Jag respekterar så mycket,

Svar

Utdata från help declare är ganska kort. En tydligare förklaring finns i man bash eller info bash – den senare är källan till det som följer.

Först några definitioner. Om variabler och attribut :

En parameter är en enhet som lagrar värden. … En variabel är en parameter betecknad med en name. En variabel har ett värde och noll eller fler attribut . Attribut tilldelas med declare inbyggt kommando …

Och om declare inbyggt :

declare

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

Förklara variabler och ge dem attribut. Om inga namn anges, visa istället värdena på variabler.

-n
Ge var och en namn attributet nameref , vilket gör det till en namnreferens till en annan variabel. Den andra variabeln definieras av värdet på namn . Alla referenser, tilldelningar och attributändringar till namn förutom de som använder eller ändrar attributet -n, utförs på variabeln som refereras av namn värde. …

Observera att namnreferens variabler endast är tillgängliga i Bash 4.3 eller senare 1 .

Dessutom, för en användbar introduktion till declare och variabla attribut i Bash skulle jag peka på att detta svara på ” Vad gör declare name och declare -g? ” (som dock huvudsakligen fokuserar på variabler ”).


I grund och botten 2 , declare name=[value] motsvarar tilldelningen name=[value] du förmodligen känner till. I båda fallen tilldelas name noll värde om value saknas.

Observera att den lite annorlunda declare name istället inte anger variabeln 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  

Således kan variabeln name:

  • deklareras och unset efter declare name;
  • deklarerat och set med null som värde, efter name= eller declare name=;
  • deklarerat , set och med ett non null värde efter name=value eller .

Mer allmänt skapar declare [options] name=value

  1. variabeln name – vilket är en parameter med ett namn, som i sin tur bara är en del av minnet som du kan använda för att lagra information 4 ;
  2. tilldelar värdet value till det;
  3. ställer valfritt name ”s attribut, som definierar både vilken typ av värde det kan lagra (inte i termer av en typ , strängt taget, eftersom Bashs språk inte skrivs) och hur det kan manipuleras.

Attribut är förmodligen lättare att förklara med ett exempel: att använda declare -i name ställer in ” heltal ” av name och låter det behandlas som ett heltal; citerar manual , ” aritmetisk utvärdering kommer att utföras när variabeln tilldelas ett värde ”:

 ## 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  

Mot bakgrund av ovanstående, vad som händer i ilkkachus kod är att:

  1. En variabel med namnet ref förklaras med ” nameref ” attributuppsättning, och innehållet i $1 (det första positioneringsargumentet) tilldelas det:

     declare -n ref="$1"  

    Syftet med en namnreferensvariabel som ref är att hålla namnet på en annan variabel, som vanligtvis inte skulle vara känd i förväg, möjligen för att vi vill att den ska vara dynamiskt definierad (t.ex. för att vi vill återanvända en bit kod och ha den tillämpas på flera variabler) och på tillhandahålla ett bekvämt sätt att hänvisa till (och manipulera) det. (Inte den enda, dock: indirektion är ett alternativ; se Skalparameterutvidgning ).

  2. När värdet på variabeln tmp1 tilldelas ref:

     ref=$tmp1  

    en ytterligare variabel, vars namn är värdet på ref, förklaras implicit. Värdet av tmp1 tilldelas också indirekt till den implicit deklarerade variabeln med hjälp av denna uttryckliga tilldelning till ref .

I samband med din länkade fråga , ring read_and_verify som

 read_and_verify domain "Prompt text here..."  

deklarerar variabeln domain och tilldela värdet tmp1 (dvs. användarens ingång). Den är exakt utformad för att återanvända koden som interagerar med användaren och utnyttja en namref-variabel att förklara domain och några andra variabler.

För att titta närmare på den implicita delen kan vi återge processen steg för steg:

 ## 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 Referens: FÖRÄNDRINGAR fil, avsnitt ” 3. Nya funktioner i Bash ”, punkt ” w ”.
Detta kan vara relevant: till exempel CentOS Linux 7.6 (för närvarande senaste versionen) levereras med Bash 4.2 .

2 Som vanligt med skalinbyggnader, en uttömmande och kortfattad förklaring är svårfångad eftersom de utför olika, möjligen heterogena åtgärder. Jag kommer att fokusera på att bara deklarera, tilldela och ställa attribut och jag kommer att överväga att lista, räkna och ta bort attribut som inte omfattas av detta svar.

3 Detta beteende för declare -p har introducerats i Bash 4.4. Referens: FÖRÄNDRINGAR , avsnitt ” 3. Nya funktioner i Bash ”, punkt ” f ”.
Som G-Man påpekade i kommentarer, i Bash 4.3 declare name; declare -p name ger ett fel. Men du kan fortfarande kontrollera att name finns med declare -p | grep "declare -- name".

4 FullBashGuide, Parametrar på mywiki.wooledge.org

Kommentarer

  • (1) Jag kan inte återge de resultat du visar i ditt första kodblock: declare name följt av declare -p name ger ”bash: declare: name: not found”. (Även om declare -p | grep na ger declare -- name.) (2) Jag tror att det är lite vilseledande att presentera echo "${name-isunset}" i samband med declare name, eftersom den behandlar en odeklarerad (dvs. odefinierad) variabel samma som en deklarerad men avstängd variabel. (3) Du kanske vill nämna att namerefs endast är tillgängliga i bash version 4.3 och senare.
  • @ G-Man Tack för dina kommentarer! Jag ’ Jag adresserar dem så snart jag kan och jag ’ uppdaterar mitt svar där det är lämpligt. När det gäller (1) ger min GNU bash, version 5.0.7(1)-release (x86_64-pc-linux-gnu) på Arch Linux fortfarande de resultat jag visade. Kanske har detta beteende introducerats först nyligen, jag ’ Jag undersöker det.
  • Ja; Jag ’ använder endast version 4.3.
  • @ G-Man Svar uppdaterat med anteckningar om (1) och (3). Om (2): Jag syftade till att illustrera att declare x inte ’ t set x, medan declare x= gör det. Jag kunde inte ’ inte hitta någon hänvisning för att hävda att declare -- x (som utdata från declare -p x) betyder ” inte inställt ”, medan declare -- x="" betyder ” ställa in ”; sålunda tog jag in ${parameter-word} expansion, även om den inte kan skilja mellan ” unset ” och ” finns inte ’ t ”, som du påpekar. Jag ’ är inte säker på hur jag kan klargöra detta i mitt svar (utan att distrahera läsaren från punkten).

Svara

Jag ska försöka förklara detta, men förlåt mig om jag inte följer det exempel du gav. Jag ska hellre försöka vägleda dig längs mitt eget, annorlunda tillvägagångssätt.

Du säger att du redan förstår begrepp som ”variabler” och ”utökar dem”, etc. så jag ska bara skumma över några bakgrundskunskap som annars skulle kräva djupare fokus.

Så jag börjar med att säga att det som mest grundläggande nivå, kommandot declare är bara ett sätt för dig att säga till Bash att du behöver ett variabelt värde (dvs. ett värde som kan ändras under körning av skript) och att du kommer att referera till det värdet med ett specifikt namn, exakt det namn du anger bredvid declare själva kommandot.

Det vill säga:

 declare foo="bar"  

säger till Bash att du vill att variabeln med namnet foo ska ha värdet bar.

Men .. vänta en minut .. vi kan gör det utan att använda declare alls, kan vi inte. Som i:

 foo="bar"  

Mycket sant.

Tja , så händer det att ovanstående enkla tilldelning faktiskt är ett implicit sätt för .. faktiskt .. deklarerar en variabel.

( Det händer också att ovanstående är en av några sätt att ändra värdet på variabeln med namnet foo; det är verkligen den mest direkta kortfattat, tydligt, rakt framåt .. men det är inte det enda .. .. Jag kommer tillbaka till detta senare .. ).

Men då, om det är så väl möjligt att deklarera ett ”namn som kommer att märka variabla värden” (bara ”variabel” hädanefter för korthets skull) utan att använda declare alls, varför skulle du någonsin vilja använda detta pompösa ”deklarera” kommando?

Svaret ligger i det faktum att ovanstående implici t sätt att deklarera en variabel (foo="bar"), gör det .. implicit .. gör att Bash anser att variabeln är av den typ som oftast används i det typiska användningsscenariot för ett skal .

En sådan typ är strängtypen, dvs. en sekvens av tecken utan någon särskild betydelse. Därför är en sträng vad du får när du använder den implicita deklarationen.

Men du, som programmerare, behöver ibland snarare betrakta en variabel som t.ex. ett tal .. som du måste göra aritmetik på operationer .. och använder en implicit deklaration som foo=5+6 kommer inte att göra att Bash tilldelar värde 11 till foo som kan du förvänta dig. Det tilldelar snarare foo sekvensen för de tre tecknen 5 + 6.

Så .. du behöver ett sätt att säga till Bash att du vill att foo ska betraktas som ett tal, inte ett sträng .. och det är vad en uttrycklig declare kommer till nytta för.

Säg bara:

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

och Bash gör gärna matematiken åt dig och tilldelar numeriskt värdet 11 till variabeln foo.

Det vill säga: genom att säga declare -i foo ger du variabel foo attribut för att vara ett heltal.

Att deklarera siffror (exakt heltal, eftersom Bash fortfarande inte förstår decimaler, flytande punkter och allt detta) kan vara den första anledningen till att använda declare, men det är inte den enda anledningen. Som du redan har förstått finns det några andra attribut som du kan ge variabler. Du kan till exempel ha Bash för att alltid göra en variabels värde stor, oavsett vad: om du säger declare -u foo, sedan från och med då när du säger foo=bar Bash tilldelar faktiskt strängen BAR till variabeln foo.

För att ge någon av dessa attribut till en variabel, du måste använda kommandot declare, det finns inget annat val.


Nu, en annan av attribut som du kan ge genom declare är den ökända “name-ref”, attributet -n. ( Och nu ska jag återuppta det koncept som jag satt i väntan tidigare ).

Attributet name-ref ger i grund och botten Bash-programmerare ett annat sätt att ändra värdet av en variabel. Det ger mer exakt ett indirekt sätt att göra det.

Här är hur det fungerar:

Du declare en variabel som har -n attributet, och det är mycket rekommenderas (men inte strikt krävs, men det gör saker enklare) att du också ger ett värde till detta mycket variabelt på samma declare kommando. Så här:

 declare -n baz="foo"  

Detta säger till Bash att från och med då på, varje gång du använder eller ändrar värdet på variabeln med namnet baz ska den faktiskt använda eller ändra värdet på variabeln med namnet foo.

Vilket betyder att, från och med då, yo du kan säga något som baz=10+3 för att foo ska få värdet 13.Självklart förutsätter vi att foo tidigare förklarades som heltal (declare -i) som vi gjorde för bara en minut sedan, annars får det sekvensen av de fyra tecknen 1 0 + 3.

Dessutom: om du ändrar foo värde direkt, som i foo=15, kommer du att se 15 också genom att säga echo “${baz}”. Detta beror på att variabel baz deklareras som namn-ref för foo alltid återspeglar foo värde.

Ovanstående declare -n -kommandot sägs vara ”namnreferens” eftersom det gör variabel baz hänvisa till namn på en annan variabel. Vi har faktiskt förklarat att baz har värdet ”foo” som, på grund av -n -alternativet, hanteras av Bash som namnet på en annan variabel.

Nu, varför på jorden skulle du någonsin vilja göra det?

Nåväl … det är värt att säga att detta är en funktion för ganska avancerade behov.

Faktiskt så avancerad att när en programmerare står inför ett problem som verkligen skulle behöva namn-ref, är det troligtvis att ett sådant problem snarare bör hanteras genom att använda ett korrekt programmeringsspråk istället för Bash.

Ett av dessa avancerade behov är till exempel när du som programmerare inte kan veta under utvecklingen vilken variabel du måste använda i en viss punkt i ett skript, men den kommer att vara fullt känd dynamiskt vid körning. Och med tanke på att det inte finns något sätt för någon programmerare att ingripa vid körning, är det enda alternativet att tillhandahålla i förväg för en sådan situation i manuset, och en ”namn-ref” kan vara den enda livskraftiga sätt. Tänk till exempel på plugin-program som ett allmänt känt användningsfall för detta avancerade behov. Programmeraren av ett ”plugin-kompatibelt” program måste göra generiska åtgärder för framtida (och eventuellt tredje part) plug-ins i förväg. Därför kommer programmeraren att behöva använda faciliteter som ett namn-ref i Bash.

Ett annat avancerat behov är när du måste hantera enorma mängder data i RAM och du också måste skicka den informationen runt funktioner i ditt skript som också måste ändra den informationen på vägen. I sådana fall kan du säkert kopiera data från en funktion till en annan (som Bash gör när du gör dest_var="${src_var}" eller när du åberopar funktioner som i myfunc "${src_var}"), men eftersom det är en enorm mängd data skulle det ge ett enormt slöseri med RAM och för en mycket ineffektiv operation. Så lösningen om sådana situationer uppstår är att inte använda en kopia av data utan en hänvisning till den informationen. I Bash, ett namn-ref. Detta användningsfall är verkligen normen i alla moderna programmeringsspråk, men det är ganska exceptionellt när det gäller Bash, för Bash är oftast utformat för korta enkla skript som mest handlar om filer och externa kommandon och därmed behöver Bash-skript sällan passera enorma mängd data mellan funktioner. Och när ett skripts funktioner behöver dela vissa data (få åtkomst till det och ändra det) uppnås detta vanligtvis genom att bara använda en global variabel, vilket är helt normen i Bash-skript lika mycket som det är mycket föråldrad på rätt programmeringsspråk.

Då kan det finnas ett anmärkningsvärt användningsfall för namn-ref i Bash, och (kanske ironiskt nog) är det associerat med när du använder ännu andra typer av variabler:

  1. variabler som deklareras som ”indexerade matriser” (declare -a)
  2. variabler som deklareras som ”associerande matriser” (declare -A).

Detta är en typ av variabler som kan lättare (liksom effektivare) skickas längs funktioner genom att använda namn-referenser istället för genom normal kopiering, även om de inte innehåller stora mängder data.

Om alla dessa exempel låter konstiga och fortfarande obegripliga är det bara för att namn-referenser verkligen är avancerat ämne och ett sällsynt behov av det typiska användningsscenariot för B Ash. rädd att om jag beskrev dem skulle jag bara komplicera saker för dig just nu när du lär dig. Bara för att nämna det minst komplexa (och möjligen inte esoteriska): att returnera värden från funktioner. Bash stöder inte riktigt denna funktionalitet, så jag fick samma genom att använda namn-ref. Detta är för övrigt exakt vad din exempelkod gör.


Förutom detta, ett litet personligt råd, som faktiskt skulle passa bättre för en kommentar men jag har inte kunnat kondensera det nog för att passa in i StackExchanges gränser för kommentar.

Jag tycker att det mesta du ska göra just nu är att bara experimentera med namn-ref genom att använda de enkla exemplen jag visade och kanske med den exempelkod du gav, utan att bortse från för tillfället ”varför på jorden” delen och fokuserar bara på “hur det fungerar” delen. Genom att experimentera lite kan ”hur” -delen sjunka bättre i ditt sinne så att ”varför” -delen kommer att bli tydlig för dig i god tid när (eller om) du får ett verkligt praktiskt problem för vilket ett namn- ref skulle verkligen komma till hands.

Kommentarer

  • LL3 Jag gillar ditt svar väldigt mycket och tummen upp: Jag förstår äntligen vad deklarera gör – – inte deklarera variabler utan deklarera deras attribut; men tyvärr tappade jag koll när du började förklara vad som är ett namn-ref. Jag fick ingen aning om vad den gör och varför jag ska använda den.
  • Det är – varför skulle man ge detta attribut
  • @JohnDoea ser jag. Du kanske bara lämnar det här ämnet för tillfället. Det är förmodligen för tidigt att förstå detta koncept vid den aktuella punkten för ditt lärande. Hur som helst har jag utvidgat mitt svar med fler exempel och även ett litet personligt tillägg till hur jag tycker att du ska gå vidare. Jag har också lagt horisontella linjer för att avgränsa den allmänna förklaringen av declare från den specifika förklaringen av -n från det personliga tillägget. Jag har också gjort lite omformulering här och där men ingenting väsentligt, så jag tror att du bara kan läsa om -n delen plus det lilla tillägget.
  • Tack, jag tror att det ’ är okej för mig att lära mig om detta just nu, jag behöver bara en förklaring som jag kan förstå och jag borde läsa igenom hela svaret idag, jag ’ är här.
  • Du nämner foo="bar" och declare foo="bar" . Du kanske vill nämna (om bara i en fotnot) att declare foo="bar" i en skalfunktion skapar en lokal variabel och foo="bar" skapar en global variabel.

Svar

I allmänhet declare bash skaluppsättningar (eller tar bort eller visar) attribut på variabler. Ett attribut är en sorts anteckning som säger ”detta är en namnreferens”, eller ”detta är en associerande matris”, eller ”denna variabel ska alltid utvärderas som ett heltal”, eller ”denna variabel är skrivskyddad och kan inte återinställas ”, eller” den här variabeln exporteras (en miljövariabel) ”etc.

typeset inbyggd är en synonym för declare i bash, eftersom typeset används i andra skal (ksh, där den har sitt ursprung, och zsh, till exempel) för att ställa in variabla attribut.


Titta närmare på exempel på namnreferens i frågan:

Skalfunktionen som du visar, med en extra kodkod som använder den:

#!/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" 

Kör detta:

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

Det visar att foo variabeln inte är inställd på någonting när användaren matar in två olika strängar.

Det visar att variabeln foo ställs in på strängen som användaren angav när de angav samma sträng två gånger .

Det sätt som $foo får värdet hello i huvuddelen av skriptet är enligt följande rader i skalfunktionen:

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

där $tmp1 är strängen hello inmatad av användaren, och $1 är strängen foo som skickas in på funktionens kommandorad från huvuddelen av script.

Observera att ref -variabeln deklareras med declare -n som en namnreferensvariabel och att värdet foo anges som värdet i den deklarationen. Detta innebär att från och med den tiden, tills variabeln går utanför räckvidden, kommer all användning av variabeln ref att vara densamma som att använda foo. Variabeln ref är en namnreferensvariabel som refererar till foo vid denna tidpunkt.

Detta har till följd att tilldelning av ett värde till ref, som görs på raden efter deklarationen, tilldelar värdet till foo.

Värdet hello är sedan tillgängligt i $foo i huvuddelen av skriptet.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *