Warum wird mein Programm "set" nicht ausgeführt?

10

Ich habe ein einfaches C-Programm wie folgt erstellt:

int main(int argc, char *argv[]) {

    if (argc != 5) {
       fputs("Not enough arguments!\n", stderr);
       exit(EXIT_FAILURE);
    }

Und ich habe meinen PATH in etc / bash.bashrc wie folgt geändert :

PATH=.:$PATH

Ich habe dieses Programm als set.c gespeichert und kompiliere es mit

gcc -o set set.c

im Ordner

~/Programming/so

Wenn ich jedoch anrufe

set 2 3

nichts passiert. Es wird kein Text angezeigt.

Berufung

./set 2 3

gibt das erwartete Ergebnis

Ich hatte noch nie ein Problem mit PATH und

which set

kehrt zurück ./set. Es scheint also, dass der PFAD der richtige ist. Was ist los?

Ganea Dan Andrei
quelle
10
Es ist relativ gefährlich, '.' zu deinem Pfad. Verwenden Sie besser einfach ./, wenn Sie etwas aus dem lokalen Verzeichnis ausführen, oder verschieben Sie die ausführbare Datei in ein bekanntes Verzeichnis wie ~ / bin /
TREE,
7
Es ist auch eine schlechte Idee, Ihr Testprogramm testaus dem gleichen Grund aufzurufen . testist auch eine Shell eingebaut.
Jonathan Leffler
@ JonathanLeffler Und doch testscheint es für schnelle Tests sinnvoll , ein Programm aufzurufen . Natürlich sollten Sie sich zu dem Zeitpunkt, an dem Sie es in Ihr PATHTelefon einfügen, einen anderen Namen einfallen lassen. Und bis Sie das Programm in Ihr Programm PATHeingefügt haben, müssen Sie es ./testtrotzdem aufrufen . Es ist also in Ordnung, den Namen testfür ein Programm zu verwenden, solange es sich um einen Schnelltest handelt, den Sie vor Tagesende löschen möchten.
Kasperd
1
@kasperd: Soweit mir bekannt ist, lautet der herkömmliche Name für ein Schnelltestprogramm foo.
Hmakholm verließ Monica
Wenn Sie es lsdann benennen, wird es ausgeführt, wenn Sie nachsehen, ob es vorhanden ist (jedoch nur, wenn Sie Ihren Pfad wie in der Frage beschrieben ändern).
Strg-Alt-Delor

Antworten:

24

Anstatt zu verwenden which, was nicht funktioniert, wenn Sie es am meisten benötigen , typebestimmen Sie mit, was ausgeführt wird, wenn Sie einen Befehl eingeben:

$ which set
./set
$ type set
set is a shell builtin

Die Shell sucht vor dem Durchsuchen immer nach eingebauten Elementen $PATH, daher $PATHhilft die Einstellung hier nicht weiter.

Es ist am besten, Ihre ausführbare Datei in etwas anderes umzubenennen. Wenn für Ihre Zuweisung jedoch der Name des Programms erforderlich ist set, können Sie eine Shell-Funktion verwenden:

$ function set { ./set; }
$ type set
set is a function
set ()
{
    ./set
}

(Das funktioniert in bash, aber andere Shells wie ksherlauben es möglicherweise nicht. Siehe mikeservs Antwort für eine tragbarere Lösung.)

Durch die Eingabe setwird nun die Funktion "set" ausgeführt, die ausgeführt wird ./set. GNU bashsucht nach Funktionen, bevor es nach Builtins sucht, und es sucht nach Builtins, bevor es nach $PATH. Weitere Informationen hierzu finden Sie im Abschnitt "BEFEHLSAUSFÜHRUNG" in der Bash-Manpage.

Siehe auch die Dokumentation zu builtinund command: help builtinund help command.

Yellowantphil
quelle
3
Sie empfehlen typeüber which, geben aber keinen Grund an, warum. ( Ich weiß warum , aber jemand, der die Empfehlung braucht, würde nicht.)
cjm
1
@cjm Hier ist eine ganze Abhandlung darüber, warum nicht welche : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan
Sehr informativ. Sie würden nie vermuten, dass es so viele Kontroversen über einen scheinbar einfachen Befehl gibt, der eine einfache Aufgabe erledigt
Ganea Dan Andrei
4
@GaneaDanAndrei Verwenden Sie hauptsächlich typestatt which, nennen Sie Ihr Programm nicht "set" und stellen Sie fest, dass dies function set { ./set; }ein hässlicher Hack ist, den Sie wahrscheinlich vermeiden sollten.
Yellowantphil
11

setist eine eingebaute Bash (und wahrscheinlich die meisten anderen Muscheln). Dies bedeutet, dass bash bei der Suche nach der Funktion nicht einmal den Pfad durchsucht.

Als Randbemerkung würde ich .aus Sicherheitsgründen dringend davon abraten , den Pfad zu erweitern. Stellen Sie sich zum Beispiel vor cd, /tmpnachdem ein anderer Benutzer eine ausführbare Datei hinzugefügt hat /tmp/cd.

klimpergeist
quelle
2
Ja, es ist eine dumme Idee, die mein Lehrer uns aufzwingt, um bei der Präsentation eines Programms nicht unprofessionell auszusehen. Er bewertet Sie, wenn es nicht getan wird.
Ganea Dan Andrei
1
Brownie Punkte für großartige Lehrer :(
klimpergeist
4
cdist eine integrierte Shell, daher funktioniert das Beispiel aus dem gleichen Grund nicht wie mit set.
Emil Jeřábek 3.0
15
Das ./fooAufrufen eines Programms ist professionell. Es zeigt, dass Sie verstehen, warum .nicht in $ PATH sein sollte. Ihr Lehrer liegt falsch, und Sie können ihm sagen, dass ich es gesagt habe.
zwol
10

setist nicht nur ein eingebautes, es ist ein POSIX-Spezial eingebaut . Es gibt einige eingebaute Befehle, die standardmäßig spezifiziert sind, um in einer Befehlssuche vor allem anderen gefunden zu werden - $PATHwerden nicht durchsucht, Funktionsnamen werden nicht durchsucht usw. Die meisten eingebauten Befehle, die nicht speziell sind, werden vom POSIX-Standard tatsächlich benötigt gefunden in Ihrem, $PATH bevor die Shell eine ihrer eigenen eingebauten Prozeduren ausführt. Dies gilt für echound die meisten anderen (obwohl , ob der Standard in dieser Hinsicht ausgezeichnet , hat in der Vergangenheit strittig an den offenen Gruppe Mailinglisten) , aber nicht von set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Oder exec.

All dies sind reservierte Namen der Shell, und sie haben andere spezielle Attribute als ihre bevorzugte Reihenfolge für die Befehlssuche. Beispielsweise können Sie in einer standardkonformen Shell keine Shell-Funktion mit einem dieser Namen definieren. Dies ist eine gute Sache - es ermöglicht Menschen, tragbare Skripte sicher zu schreiben . Dies sind Basisbefehle, mit denen ein erfahrener Drehbuchautor einen sicheren und zuverlässigen Halt in seiner Umgebung finden kann. Das Eindringen in diesen Namespace ist nicht ratsam.

Wenn Sie jedoch in das Gebiet eindringen möchten, können Sie dies portabel mit tun alias. Die Reihenfolge der Shell- Erweiterung ermöglicht diese Umgehung. Da das aliaserweitert wird, während der Befehl gelesen wird, wird alles, durch das Sie den setNamen in Ihrer Definition ersetzen , korrekt erweitert. Es sollte nur wahrscheinlich nicht auf einen dieser Namen erweitert werden.

So könnten Sie tun:

alias set=./set

... was gut funktionieren wird.

mikeserv
quelle
3

Das Problem ist, dass seteine Shell eingebaut ist und die beste Lösung darin besteht, einen anderen Namen für Ihr ausführbares Programm zu verwenden.

Übrigens habe ich letzte Woche eine Frage gestellt, wie Systembefehle anstelle von Shell-Builds mit demselben Namen ausgeführt werden sollen. Die Lösung, die ich akzeptiert habe, bestand darin, den Befehl auszuführen env:

env set 2 3

In diesem speziellen Fall, in dem Sie bereits wissen, dass sich der zu verwendende Befehl in Ihrem aktuellen Verzeichnis befindet, ist es besser, die ausführbare Datei direkt auszuführen, indem Sie ihren Pfad eingeben ( .um das aktuelle Arbeitsverzeichnis darzustellen):

./set 2 3

Beide oben genannten Lösungen sind Shell-unabhängig, dh sie funktionieren unabhängig davon, welche Shell Sie verwenden.

Vorschläge wie die Verwendung des integrierten commandSystems funktionieren in Bash nicht: Dies verhindert nur, dass Shell- Funktionen ausgeführt werden. Es ist zwar nicht dokumentiert, aber ich habe auch festgestellt, dass die Verwendung commandauch Shell- Schlüsselwörter unterdrückt . Bei Shell- Builds wie z set. Soweit ich weiß, commandkann mit anderen Shells wie zsh arbeiten.

Auch Tricks wie \setoder "set"oder 'set'nicht Arbeit für Bash builtins - obwohl sie für die Ausführung von ausführbaren Dateien statt nützlich sind Aliase oder Shell - Schlüsselwörter .

Hinweis: Diese Antwort begann ursprünglich als Kommentar zu Erics (akzeptierter) Antwort, wurde jedoch zu groß, um in einen Kommentar zu passen. Die anderen Antworten, die die Verwendung des PATH empfehlen typeund ihn nicht .ergänzen, sind gut.

Anthony Geoghegan
quelle