Hur kan jag testa om en variabel är tom eller endast innehåller mellanslag?

Följande bash-syntax verifierar om param inte är tom:

 [[ ! -z $param ]] 

Till exempel:

param="" [[ ! -z $param ]] && echo "I am not zero" 

Ingen utdata och dess bra.

Men när param är tom förutom ett (eller flera) mellanslagstecken, då är fallet annorlunda:

param=" " # one space [[ ! -z $param ]] && echo "I am not zero" 

”Jag är inte zero ”matas ut.

Hur kan jag ändra testet så att variabler som endast innehåller mellanslagstecken är tomma?

Kommentarer

  • Från man test: -z STRING - the length of STRING is zero. Om du vill ta bort alla mellanslag i $param, använd ${param// /}
  • WOW det finns ingen enkel trim() -funktion inbyggd för någon * nix alls? Så många olika hack för att uppnå något så enkelt …
  • @ADTC $ (sed ’ s / ^ \ s + | \ s + $ // g ’ < < < $ string) verkar vara tillräckligt enkelt för mig. Varför uppfinna hjulet på nytt?
  • Eftersom det kan vara blankare
  • Att bara notera att [[ ! -z $param ]] motsvarar test ! -z $param.

Svar

Observera först att -z test är uttryckligen för:

strängens längd är noll

Det vill säga en sträng som endast innehåller mellanslag ska inte vara sant under -z, eftersom den har en längd som inte är noll.

Vad du vill är att ta bort mellanslag från variabeln med expansion av mönsterparameterparameter :

[[ -z "${param// }" ]] 

Detta utvidgar param variabel och ersätter alla matchningar i mönstret (ett enda mellanslag) med ingenting, så en sträng som bara har mellanslag utvidgas till en tom sträng.


nitty-gritty om hur det fungerar är att ${var/pattern/string} ersätter den första längsta matchningen av pattern med string. När pattern börjar med / (som ovan) ersätter det alla matchningarna. Eftersom ersättaren är tom kan vi utelämna det slutliga / och string -värdet:

$ {parameter / mönster / sträng}

mönstret utvidgas för att producera ett mönster precis som vid filnamnsexpansion. Parameter utvidgas och den längsta matchningen av mönster mot dess värde ersätts med sträng . Om mönster börjar med “/” ersätts alla matchningar av mönster med sträng . Normalt ersätts bara den första matchen. … Om sträng är noll raderas matchningar av mönster och / följande mönster kan utelämnas.

När allt kommer omkring slutar vi med ${param// } för att ta bort alla mellanslag.

Observera att dock närvarande i ksh (där det härstammar), zsh och bash, att syntax inte är POSIX och ska inte användas i sh skript.

Kommentarer

  • använd sed sa de … bara echo variabeln de sa …
  • Hmm. Om det är ${var/pattern/string} borde ’ t vara [[ -z ${param/ /} ]] med mellanslag mellan snedstreck att vara mönstret och ingenting efter att vara strängen?
  • @JesseChisholm ” Eftersom ersättningen är tom kan vi utelämna det slutliga / och strängvärdet ”; ” Om sträng är noll raderas matchningar av mönster och / följande mönster kan raderas utelämnad. ”
  • @MichaelHomer Svaret [[ -z "${param// }" ]] ser verkligen ut som pattern är tomt och replacement string är ett enda mellanslag. AH! Jag ser det nu if pattern begins with a slash Jag saknade den delen. så mönstret är / och strängen är avstängd och därför tom. Tack.
  • bash-anmärkning: om du kör i set -o substantivset (set -u), och param är avmarkerat (snarare än null eller mellanslag), genererar detta ett obundet variabelt fel. (set + u; …..) skulle undanta detta test.

Svar

Det enkla sättet att kontrollera att en sträng endast innehåller tecken i en auktoriserad uppsättning är att testa om det finns obehöriga tecken.Så istället för att testa om strängen bara innehåller mellanslag, testa om strängen innehåller något annat tecken än mellanslag. I bash, ksh eller zsh:

if [[ $param = *[!\ ]* ]]; then echo "\$param contains characters other than space" else echo "\$param consists of spaces only" fi 

”Endast består av mellanslag” inkluderar fallet med en tom (eller avstängd) variabel.

Du kanske vill testa om du vill ha ett mellanslagstecken. Använd [[ $param = *[^[:space:]]* ]] för att använda språkinställningar, eller vilken uttrycklig lista som helst med blankstegstecken du vill testa, t.ex. [[ $param = *[$" \t\n"]* ]] för att testa för utrymme, flik eller ny linje.

Matcha en sträng mot ett mönster med = inuti [[ … ]] är en ksh-förlängning (finns även i bash och zsh). I valfri Bourne / POSIX-stil kan du använda case -konstruktionen för att matcha en sträng mot ett mönster. Observera att vanliga skalmönster använder ! för att negera en teckenuppsättning, snarare än ^ som i de vanligaste uttryckssyntaxerna.

case "$param" in *[!\ ]*) echo "\$param contains characters other than space";; *) echo "\$param consists of spaces only";; esac 

För att testa för mellanslagstecken är $"…" syntax specifik för ksh / bash / zsh; Du kan infoga dessa tecken i ditt skript bokstavligen (notera att en ny rad måste vara inom citattecken, eftersom bakåtstreck + ny linje expanderar till ingenting), eller generera dem, t.ex.

whitespace=$(printf "\n\t ") case "$param" in *[!$whitespace]*) echo "\$param contains non-whitespace characters";; *) echo "\$param consists of whitespace only";; esac 

Kommentarer

  • Detta är nästan utmärkt – men det svarar ännu inte på frågan som ställd. Det kan för närvarande berätta skillnaden mellan en avstängd eller tom skalvariabel och en som endast innehåller mellanslag. Om du accepterar mitt förslag kommer du att lägga till param-expansionsformuläret som ännu inte konverterats av något annat svar som uttryckligen testar en skalvariabel för att vara inställd – jag menar ${var?not set} – att klara testet och överleva skulle säkerställa att en variabel som matchas av din *) case till exempel skulle ge ett definitivt svar.
  • Korrigering – i själva verket skulle svara på den fråga som ursprungligen ställdes, men den har sedan redigerats på ett sådant sätt att den i grunden förändrar innebörden av frågan och därför ogiltigförklarar ditt svar.
  • Du behöver [[ $param = *[!\ ]* ]] i mksh.

Svar

POSIXly:

case $var in (*[![:blank:]]*) echo "$var contains non blank";; (*) echo "$var contains only blanks or is empty or unset" esac 

För att skilja mellan blank , icke-tom , tom , unset :

case ${var+x$var} in (x) echo empty;; ("") echo unset;; (x*[![:blank:]]*) echo non-blank;; (*) echo blank esac 

[:blank:] är för horisontella avståndstecken (mellanslag och flik i A. SCII, men det finns förmodligen några fler i din lokal; vissa system kommer att innehålla det icke-brytande utrymmet (om tillgängligt), vissa vann ”t). Om du också vill ha vertikala avståndstecken (som newline eller form-feed), ersätt [:blank:] med [:space:].

Kommentarer

  • POSIXly är perfekt eftersom det fungerar med (utan tvekan bättre) streck.
  • zsh verkar ha problem med [![:blank:]], det höjer :b är ogiltig modifierare. I sh och ksh emulerar det den händelse som inte hittades för [
  • @cuonglm, vad försökte du? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac' är OK för mig. Händelsen som inte hittades skulle bara vara för interaktiva skal.
  • @St é phaneChazelas: Ah, höger, jag försökte med interaktiva skal. :b ogiltig modifierare är lite konstig.
  • @FranklinYu, på OS / X sh är byggd med --enable-xpg-echo-default och --enable-strict-posix-default för att vara Unix-kompatibel (vilket involverar echo '\t' mata ut en flik).

Svar

Den enda återstående anledningen att skriva ett skalskript istället för ett skript i ett bra skriptspråk, är om extrem bärbarhet är en övergripande fråga. Äldre /bin/sh är det enda du kan vara säker på att du har, men Perl är till exempel mer troligtvis tillgänglig över plattformen än Bash. Skriv därför aldrig skalskript som använder funktioner som inte är ”t verkligt universella – och kom ihåg att flera proprietära Unix-leverantörer frös sin skalmiljö före POSIX.1-2001 .

Det finns ett bärbart sätt att göra detta test, men du måste använda tr:

[ "x`printf "%s" "$var" | tr -d "$IFS"`" = x ] 

(Standardvärdet för $IFS är bekvämt ett mellanslag, en flik och en ny rad.)

(printf inbyggt är i sig själva bärbart, men att förlita sig på det är mycket mindre krångel än att ta reda på vilken variant av echo du har.)

Kommentarer

  • Att ’ är lite invecklad.Det vanliga bärbara sättet att testa om en sträng innehåller vissa tecken är med case .
  • @Gilles Jag tror inte ’ det ’ är möjligt att skriva en case glob till upptäcka en sträng som antingen är tom eller som endast innehåller mellanslagstecken. (Det skulle vara enkelt med en regexp, men case ’ t gör regexps. Konstruktionen i ditt svar vann ’ fungerar inte om du bryr dig om nya rader.)
  • Jag gav mellanslag som ett exempel i mitt svar eftersom ’ är vad frågan bad om. Det är ’ lika lätt att testa för mellanslagstecken: case $param in *[![:space:]]*) …. Om du vill testa för IFS tecken kan du använda mönstret *[$IFS]*.
  • @mikeserv In a allvarligt defensivt skript, du ställer uttryckligen IFS till <space><tab><newline> i början och rör sedan aldrig igen. Jag trodde att ’ skulle vara en distraktion.
  • När det gäller variabler i mönster, tror jag att det fungerar i alla Bourne-versioner och alla POSIX-skal; det skulle kräva extra kod för att upptäcka ärendemönster och stänga av variabel expansion och jag kan ’ inte tänka mig en anledning att göra det. Jag har ett par exempel på det i min .profile som har utförts av många Bourne-skal. Naturligtvis [$IFS] vann ’ t gör vad som var avsett om IFS har vissa icke-standard värden (tomma (eller avmarkerade), som innehåller ], börjar med ! eller ^) .

Svar

if [[ -n "${variable_name/[ ]*\n/}" ]] then #execute if the the variable is not empty and contains non space characters else #execute if the variable is empty or contains only spaces fi 

Kommentarer

  • detta blir av med något vitt utrymme, inte bara ett enda mellanslag, eller hur? tack

Svar

För att testa om variabeln är tom eller innehåller mellanslag kan du också använda den här koden:

${name:?variable is empty} 

Kommentarer

  • Jag tror att ditt svar är nedröstat eftersom ” eller innehålla mellanslag ” är inte sant.
  • var försiktig – det dödar det aktuella skalet. Bättre att sätta det i en (: $ {subshell?}). Också – det kommer att skriva till stderr – du vill antagligen omdirigera. Och det viktigaste som utvärderas till variabeln ’ s verkliga värde om den inte är inställd eller null. Om det finns ’, körde du det bara. ’ är bäst att köra testet på :.
  • hur det kommer att döda skalet. och du kan också testa detta, först definiera name=aaaa kör sedan kommandot echo ${name:?variable is empty} det skriver ut värdet på variabelnamnet och kör nu det här kommandot echo ${name1:?variable is empty} den kommer att skriva ut -sh: name1: variabel är tom
  • Ja – echo ${name:?variable is empty} antingen utvärderas till $name ’ s icke-nollvärde eller det kommer att döda skalet. Av samma anledning gör du inte ’ t prompt > $randomvar ska du inte heller ${var?} där ’ ett kommando där inne kommer det ’ förmodligen att springa – och du ’ vet inte vad det är. Ett interaktivt skal behöver inte ’ sluta – det är därför ditt inte ’ t. Ändå, om du vill se några tester finns det många av dem här. . Den här är inte ’ t lika länge …

Svar

Koden du skickade [[ ! -z $param ]] && echo "I am not zero" skriver ut I am not zero om param är antingen avmarkerad / odefinierad eller tom (i bash, ksh och zsh).

Till också skriv ut om parametern endast innehåller mellanslag (ta bort mellanslag), POSIXly (för ett större antal skal):

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty" 

Förklaring:

  • A ${param# … } tar bort en ledande text.
  • Den ledande texten ges av: ${param%%[! ]*}
  • Vilket expanderar till alla mellanslag före alla icke-mellanslag karaktär, dvs. alla ledande mellanslag.

Slutresultatet av hela expansionen är att alla ledande utrymmen tas bort.

Detta utvidgade resultat testas om det är längd 0.

  • Om resultatet är tomt var antingen variabeln tom eller
  • avlägsnande av de ledande mellanslagen gjorde det tomt.

Om testet är sant var variabeln antingen redan tom eller hade bara mellanslag inuti.


Konceptet är lätt att utvidga till mer karaktärstyper. För att även inkludera flikar placerar du en uttrycklig flik i parentesuttrycket:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty" 

eller använder en variabel med de tecken du behöver ta bort:

 var=$" \t" # in ksh, bash, zsh [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty" 

eller så kan du använda något POSIX ”teckenklassuttryck” (tomt betyder mellanslag och flik):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty" 

Svar

För att kontrollera om variabel endast har mellanslag, inklusive i en variabel med flera rader , prova detta:

[[ $var = *[$" \t\n"]* ]] 

Eller med xargs:

[[ -z $(echo $var | xargs) ]] 

Eller med sed:

[[ -z $(sed "/^$/d" <<< $var) ]] 

Svar

Prova detta:

$ [[ -z \`echo $n\` ]] && echo zero zero 

Lämna ett svar

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