Hoe kan ik valideren dat een programma bestaat, op een manier die een foutmelding terugzet en afsluit, of doorgaan met het script?
Het lijkt erop dat het gemakkelijk zou moeten zijn, maar het heeft me gestoken.
Hoe kan ik valideren dat een programma bestaat, op een manier die een foutmelding terugzet en afsluit, of doorgaan met het script?
Het lijkt erop dat het gemakkelijk zou moeten zijn, maar het heeft me gestoken.
POSIX compatibel:
command -v <the_command>
Voor bash
specifieke omgevingen:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
vermijden which
. Het is niet alleen een extern proces dat je lanceert om heel weinig te doen (wat betekent dat het gebouwd is hash
, type
of command
zijn veel goedkoper), u kunt ook vertrouwen op de ingebouwde functies om daadwerkelijk te doen wat u wilt, terwijl de effecten van externe opdrachten gemakkelijk kunnen variëren van systeem tot systeem.
Waarom zorg?
which
dat stelt zelfs geen exit-status in, wat betekent dat if which foo
zal daar niet eens werken altijd rapporteer dat foo
bestaat, zelfs als dat niet het geval is (merk op dat sommige POSIX-shells dit lijken te doen hash
te).which
doe aangepaste en kwade dingen zoals het wijzigen van de uitvoer of zelfs inhaken op de pakketbeheerder.Dus gebruik het niet which
. Gebruik in plaats daarvan een van deze:
$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed. Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed. Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed. Aborting."; exit 1; }
(Kleine kanttekening: sommigen zullen voorstellen 2>&-
is hetzelfde 2>/dev/null
maar korter - dit is niet waar. 2>&-
sluit FD 2 af waardoor er een fout in het programma wanneer het probeert te schrijven naar stderr, wat erg verschilt van succesvol schrijven en het weggooien van de uitvoer (en gevaarlijk!))
Als je Hash-knal is /bin/sh
dan zou je moeten geven om wat POSIX zegt. type
en hash
's exit codes zijn niet erg goed gedefinieerd door POSIX, en hash
wordt gezien als succesvol afgesloten als het commando niet bestaat (dit niet gezien type
nog). command
De exit-status is goed gedefinieerd door POSIX, dus waarschijnlijk is dit de veiligste om te gebruiken.
Als je script gebruikt bash
POSIX-regels maken echter niet echt meer uit en beide type
en hash
volkomen veilig in gebruik. type
heeft nu een -P
om alleen de PATH
en hash
heeft het neveneffect dat de locatie van het commando gehasht zal worden (voor een snellere opzoeking de volgende keer dat je het gebruikt), wat meestal een goede zaak is, omdat je waarschijnlijk het bestaan ervan controleert om het daadwerkelijk te gebruiken.
Als een eenvoudig voorbeeld, hier is een functie die wordt uitgevoerd gdate
als het bestaat, anders date
:
gnudate() {
if hash gdate 2>/dev/null; then
gdate "$@"
else
date "$@"
fi
}
Het volgende is een draagbare manier om te controleren of een commando bestaat in $PATH
en is uitvoerbaar:
[ -x "$(command -v foo)" ]
Voorbeeld:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
De uitvoerbare controle is nodig omdat bash een niet-uitvoerbaar bestand retourneert als er geen uitvoerbaar bestand met die naam wordt gevonden $PATH
.
Merk ook op dat er eerder een niet-uitvoerbaar bestand met dezelfde naam als het uitvoerbare bestand bestaat $PATH
, streepje geeft de eerste terug, ook al zou de laatste worden uitgevoerd. Dit is een bug en is in overtreding met de POSIX-standaard. [Bug report] [Standaard]
Bovendien zal dit mislukken als het commando dat u zoekt is gedefinieerd als een alias.
Ik ben het eens met lhunath om het gebruik van te ontmoedigen which
, en zijn oplossing is volkomen geldig voor BASH-gebruikers. Om draaglijker te zijn, command -v
zal in plaats daarvan worden gebruikt:
$ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed. Aborting." >&2; exit 1; }
Opdracht command
voldoet aan POSIX, kijk hier voor de specificatie: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
Notitie: type
is POSIX-compatibel, maar type -P
is niet.
Ik heb een functie gedefinieerd in mijn .bashrc die dit gemakkelijker maakt.
command_exists () {
type "$1" &> /dev/null ;
}
Hier is een voorbeeld van hoe het wordt gebruikt (van mijn .bash_profile
.)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
Het hangt ervan af of u wilt weten of het bestaat in een van de mappen in de $PATH
variabele of dat u de absolute locatie ervan kent. Als je wilt weten of het in de $PATH
variabele, gebruik
if which programname >/dev/null; then
echo exists
else
echo does not exist
fi
anders gebruik
if [ -x /path/to/programname ]; then
echo exists
else
echo does not exist
fi
De doorverwijzing naar /dev/null/
in het eerste voorbeeld onderdrukt de uitvoer van de which
programma.
Uitbreiding op de antwoorden van @ lhunath en @ GregV, hier is de code voor de mensen die deze controle gemakkelijk in een if
uitspraak:
exists()
{
command -v "$1" >/dev/null 2>&1
}
Hier is hoe het te gebruiken:
if exists bash; then
echo 'Bash exists!'
else
echo 'Your system does not have Bash'
fi
Probeer het gebruik van:
test -x filename
of
[ -x filename ]
Van de bash manpage onder Voorwaardelijke uitdrukkingen:
-x file True if file exists and is executable.
Gebruiken hash
, als @lhunath suggereert, in een bash-script:
hash foo &> /dev/null
if [ $? -eq 1 ]; then
echo >&2 "foo not found."
fi
Dit script wordt uitgevoerd hash
en controleert vervolgens of de afsluitcode van de meest recente opdracht, de waarde die is opgeslagen in $?
, is gelijk aan 1
. Als hash
vindt het niet foo
, de exit-code zal zijn 1
. Als foo
is aanwezig, de exit-code zal zijn 0
.
&> /dev/null
stuurt standaardfout en standaarduitvoer om van hash
zodat het niet op het scherm verschijnt en echo >&2
schrijft het bericht naar standaardfout.
Ik heb nooit de bovenstaande oplossingen laten werken op de doos waar ik toegang tot heb. Ten eerste is type geïnstalleerd (doen wat meer doet). Dus de ingebouwde richtlijn is nodig. Deze opdracht werkt voor mij:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
Als je controleert of het programma bestaat, ga je het waarschijnlijk toch later gebruiken. Waarom probeert u het niet in de eerste plaats?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
Het is een meer betrouwbare controle dat het programma wordt uitgevoerd dan alleen kijken naar PATH-mappen en bestandsrechten.
Bovendien kunt u een nuttig resultaat van uw programma krijgen, zoals de versie ervan.
Natuurlijk zijn de nadelen dat sommige programma's zwaar kunnen zijn om te starten en sommige geen --version
optie om onmiddellijk (en met succes) af te sluiten.