Vraag Controleer of de array leeg is in Bash


Ik heb een array die wordt gevuld met verschillende foutmeldingen wanneer mijn script wordt uitgevoerd.

Ik heb een manier nodig om te controleren of deze leeg is, niet aan het einde van het script en neem een ​​specifieke actie als dat zo is.

Ik heb al geprobeerd het als een normale VAR te behandelen en -z te gebruiken om het te controleren, maar dat lijkt niet te werken. Is er een manier om te controleren of een array leeg is of niet in Bash?

Bedankt


82
2018-02-11 03:59


oorsprong




antwoorden:


Stel dat je array is $errors, controleer gewoon om te zien of het aantal elementen nul is.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

113
2018-02-11 04:10



Houd er rekening mee dat = is een string-operator. Het werkt in dit geval prima, maar ik zou de juiste rekenkundige operator gebruiken -eq in plaats daarvan (voor het geval ik zou willen overschakelen naar -ge of -lt, enz.). - musiphil
Werkt niet met set -u: "niet-afhankelijke variabele" - als de array leeg is. - Igor
@Igor: Werkt voor mij in Bash 4.4. set -u;  foo=();  [ ${#foo[@]} -eq 0 ] && echo empty. Als ik unset foo, dan drukt het foo: unbound variable, maar dat is anders: de array-variabele bestaat helemaal niet, in plaats van bestaande en is leeg. - Peter Cordes
Ook getest in Bash 3.2 (OSX) bij gebruik set -u - Zolang je je variabele als eerste hebt opgegeven, werkt dit perfect. - zeroimpl


U kunt de array ook beschouwen als een eenvoudige variabele. Op die manier alleen gebruiken

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

of de andere kant gebruiken

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

Het probleem met die oplossing is dat als een array als volgt wordt verklaard: array=('' foo). Deze controles zullen de array als leeg rapporteren, terwijl dit duidelijk niet het geval is. (bedankt @musiphil!)

Gebruik makend van [ -z "$array[@]" ] is ook duidelijk geen oplossing. Het niet opgeven van accolades probeert te interpreteren $array als een string ([@] is in dat geval een eenvoudige letterlijke reeks) en wordt daarom altijd als onwaar gerapporteerd: "is de letterlijke tekenreeks [@] leeg? "Duidelijk niet.


6
2018-06-23 10:29



[ -z "$array" ] of [ -n "$array" ] werkt niet. Proberen array=('' foo); [ -z "$array" ] && echo emptyen het wordt afgedrukt empty Hoewel array is duidelijk niet leeg. - musiphil
[[ -n "${array[*]}" ]] interpoleert de gehele array als een tekenreeks, die u controleert op niet-nullengte. Als je overweegt array=("" "") om leeg te zijn, in plaats van twee lege elementen te hebben, kan dit nuttig zijn. - Peter Cordes


Ik gebruik over het algemeen rekenkundige uitbreiding in dit geval:

if (( ${#a[@]} )); then
    echo not empty
fi

3
2017-08-02 06:04



Mooi en schoon! Ik vind het leuk. Ik merk ook op dat als het eerste element van de array altijd niet-leeg is, (( ${#a} )) (lengte van het eerste element) zal ook werken. Dat zal echter falen a=(''), terwijl (( ${#a[@]} )) gegeven in het antwoord zal slagen. - cxw


Ik heb het nagekeken bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

en bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

In het laatste geval hebt u de volgende constructie nodig:

${array[@]:+${array[@]}}

om niet te falen op een lege of niet-geplaatste array. Dat is als je dat doet set -eu zoals ik gewoonlijk doe. Dit zorgt voor een meer strikte foutcontrole. Van de documenten:

-e

Sluit onmiddellijk af als een pijplijn (zie Pijplijnen), die uit een enkele eenvoudige opdracht (zie Eenvoudige opdrachten), een lijst (zie Lijsten) of een samengestelde opdracht (zie Samengestelde opdrachten) bestaat, een niet-nul-status retourneert. De shell wordt niet afgesloten als de opdracht die mislukt deel uitmaakt van de opdrachtenlijst onmiddellijk na een tijdje of tot het trefwoord, een deel van de test in een if-instructie, onderdeel van een opdracht die wordt uitgevoerd in een && of || lijst, behalve de opdracht na de laatste && of ||, een opdracht in een pijplijn, maar de laatste, of als de retourstatus van de opdracht wordt omgekeerd met!. Als een samengestelde opdracht anders dan een subschaal een niet-nulstatus retourneert omdat een opdracht is mislukt terwijl -e werd genegeerd, wordt de shell niet afgesloten. Een trap op ERR, indien ingesteld, wordt uitgevoerd voordat de shell wordt afgesloten.

Deze optie is van toepassing op de shellomgeving en elke subshellomgeving afzonderlijk (zie de opdrachtuitvoeringsomgeving) en kan ervoor zorgen dat de subshells worden afgesloten voordat alle opdrachten in de submap worden uitgevoerd.

Als een samengestelde opdracht of shell-functie wordt uitgevoerd in een context waarin -e wordt genegeerd, wordt geen van de opdrachten die worden uitgevoerd in het samengestelde commando of het hoofdgedeelte van de functie beïnvloed door de instelling -e, zelfs als -e is ingesteld en een opdracht een a retourneert foutstatus. Als een samengestelde opdracht of shell-functie -e instelt tijdens het uitvoeren in een context waarin -e wordt genegeerd, heeft die instelling geen effect totdat de samengestelde opdracht of de opdracht die de functie-oproep bevat, is voltooid.

-u

Behandel afwijkende variabelen en parameters anders dan de speciale parameters '@' of '*' als een fout bij het uitvoeren van parameteruitbreiding. Er wordt een foutbericht naar de standaardfout geschreven en een niet-interactieve shell wordt afgesloten.

Als je dat niet nodig hebt, voel je dan vrij om weg te laten :+${array[@]} een deel.

Merk ook op dat het essentieel is om te gebruiken [[ operator hier, met [ Jij krijgt:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

2
2017-12-04 14:58



Met -u je zou eigenlijk moeten gebruiken ${array[@]+"${array[@]}"} cf stackoverflow.com/a/34361807/1237617 - Jakub Bochenski
@JakubBochenski Over welke versie van bash heb je het? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9 - x-yuri
Het probleem in het voorbeeld met enkele haakjes is het @, zeker. Je zou kunnen gebruiken * array-uitbreiding zoals [ "${array[*]}" ], kun je niet? Nog steeds, [[ werkt ook goed. Het gedrag van beide voor een array met meerdere lege tekenreeksen is een beetje verrassend. Beide [ ${#array[*]} ] en [[ "${array[@]}" ]] zijn vals voor array=() en array=('') maar waar voor array=('' '') (twee of meer lege reeksen). Als je wilde dat een of meer lege reeksen allemaal waar zouden zijn, zou je kunnen gebruiken [ ${#array[@]} -gt 0 ]. Als je ze allemaal vals had, zou je dat misschien kunnen // ze uit. - eisd
@eisd zou ik kunnen gebruiken [ "${array[*]}" ], maar als ik zo'n uitdrukking zou tegenkomen, zou het moeilijker voor me zijn om te begrijpen wat het doet. Sinds [...] werkt in termen van strings op het resultaat van interpolatie. In tegenstelling tot [[...]], die op de hoogte kan zijn van wat werd geïnterpoleerd. Dat wil zeggen, het kan weten dat het een array is gepasseerd. [[ ${array[@]} ]] leest mij voor als "controleer of array niet leeg is", terwijl [ "${array[*]}" ] als "controleer of het resultaat van interpolatie van alle arrayelementen een niet-lege reeks is". - x-yuri
... Wat betreft gedrag met twee lege snaren is het helemaal niet verrassend voor mij. Wat verrassend is, is het gedrag met één lege string. Maar aantoonbaar redelijk. met betrekking tot [ ${#array[*]} ], wat je waarschijnlijk bedoelde [ "${array[*]}" ], omdat de eerste waar is voor een onbeperkt aantal elementen. Omdat het aantal elementen altijd een niet-lege reeks is. Wat betreft het laatste met twee elementen, breidt de uitdrukking tussen haakjes uit naar ' ' welke niet-lege string is. Wat betreft [[ ${array[@]} ]], ze denken (en terecht) dat een reeks van twee elementen niet leeg is. - x-yuri


In mijn geval, de tweede antwoord was niet genoeg, want er kunnen spaties zijn. Ik kwam langs met:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

0
2018-02-17 19:54



echo | wc lijkt onnodig inefficiënt vergeleken met het gebruik van ingebouwde shell-ins. - Peter Cordes
Ik weet niet zeker of ik @PeterCordes begrijp, kan ik de tweede antwoorden aanpassen ' [ ${#errors[@]} -eq 0 ]; op een manier om het witruimteprobleem te omzeilen? Ik zou ook de voorkeur geven aan de ingebouwde. - Micha
Hoe veroorzaakt witruimte precies een probleem? $# breidt uit naar een getal en werkt ook prima na opts+=(""). bijv. unset opts;  opts+=("");opts+=(" "); echo "${#opts[@]}" en ik krijg 2. Kun je een voorbeeld laten zien van iets dat niet werkt? - Peter Cordes
Het is lang geleden. IIRC de oorspronkelijke bron altijd ten minste "" afgedrukt. Dus, voor opts = "" of opts = ("") heb ik 0, niet 1 nodig, de lege newline of lege string negerend. - Micha
Ok, dus je moet behandelen opts=("") hetzelfde als opts=()? Dat is geen lege array, maar u kunt controleren op lege array of leeg eerste element met opts=("");  [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty. Merk op dat je huidige antwoord "geen opties" zegt voor opts=("" "-foo"), wat volkomen nep is, en dit reproduceert dat gedrag. Je zou kunnen [[ -z "${opts[*]}" ]] Ik veronderstel, alle elementen van de array interpoleren in een platte string, die -z controleert op niet-nullengte.  Als het controleren van het eerste element voldoende is, -z "$opts" werken. - Peter Cordes


Ik gebruik liever dubbele haakjes:

if [[ !${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi

Dubbele haakjes: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash


0
2017-11-04 06:40