Dlaczego mój program o nazwie „set” nie jest wykonywany?

10

Stworzyłem prosty program C taki jak:

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

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

I mam zmienioną PATH w etc / bash.bashrc w następujący sposób:

PATH=.:$PATH

Zapisałem ten program jako set.c i kompiluję go

gcc -o set set.c

w folderze

~/Programming/so

Jednak kiedy dzwonię

set 2 3

nic się nie dzieje. Brak wyświetlanego tekstu.

Powołanie

./set 2 3

daje oczekiwany wynik

Nigdy wcześniej nie miałem problemu z PATH

which set

zwraca ./set. Wygląda więc na to, że ŚCIEŻKA jest poprawna. Co się dzieje

Ganea Dan Andrei
źródło
10
Dodanie „” jest stosunkowo niebezpieczne. do twojej ŚCIEŻKI. Lepiej jest użyć ./ podczas wykonywania czegoś z katalogu lokalnego lub przenieść plik wykonywalny do znanego katalogu, takiego jak ~ / bin /
TREE
7
Nie jest również dobrym pomysłem wywoływanie programu testowego testz tego samego powodu; testjest również wbudowany w powłokę.
Jonathan Leffler,
@JonathanLeffler A jednak szybkie testy testwydają się mieć sens. Oczywiście, zanim go włożysz PATH, naprawdę powinieneś wymyślić inne imię. I dopóki nie włożysz programu do swojego programu, PATHbędziesz musiał go w ./testkażdym razie wywołać . Dlatego używanie nazwy testprogramu jest w porządku, o ile jest to szybki test, który zamierzasz usunąć przed końcem dnia.
kasperd
1
@kasperd: O ile mi wiadomo, konwencjonalna nazwa szybkiego programu testowego to foo.
hmakholm opuścił Monikę
Jeśli go lsnazwiesz, to za każdym razem, gdy zobaczysz, czy istnieje, uruchomi się (ale tylko jeśli zmienisz ścieżkę, tak jak w pytaniu).
ctrl-alt-delor

Odpowiedzi:

24

Zamiast używać which, która nie działa, gdy jest najbardziej potrzebna , użyj, typeaby określić, co będzie działać po wpisaniu polecenia:

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

Powłoka zawsze szuka wbudowanych przed przeszukaniem $PATH, więc ustawienie $PATHtutaj nie pomaga.

Najlepiej byłoby zmienić nazwę pliku wykonywalnego na coś innego, ale jeśli twoje przypisanie wymaga nazwy programu set, możesz użyć funkcji powłoki:

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

(To działa bash, ale inne podobne powłoki kshmogą na to nie zezwalać. Zobacz odpowiedź mikeserv na bardziej przenośne rozwiązanie).

Teraz podczas wpisywania seturuchomiona zostanie funkcja o nazwie „set”, która zostanie wykonana ./set. GNU bashszuka funkcji przed szukaniem wbudowanych i szuka wbudowanych przed przeszukaniem $PATH. Sekcja o nazwie „WYKONANIE POLECEŃ” na stronie podręcznika użytkownika bash zawiera więcej informacji na ten temat.

Zobacz także dokumentację builtini command: help builtini help command.

żółcień
źródło
3
Polecasz typeponad which, ale nie podawaj żadnego powodu. ( Wiem dlaczego , ale ktoś, kto potrzebuje rekomendacji, nie zrobiłby tego).
cjm
1
@cjm Oto cały traktat na temat tego, dlaczego nie : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan
Bardzo informujące. Nigdy nie zgadniesz, że istnieje tak wiele kontrowersji dotyczących tak pozornie prostego polecenia, które wykonuje proste zadanie
Ganea Dan Andrei
4
@GaneaDanAndrei Głównie używaj typezamiast which, nie nazywaj swojego programu „set” i zdaj sobie sprawę, że function set { ./set; }to brzydki hack, którego prawdopodobnie powinieneś unikać.
yellowantphil
11

setjest wbudowany w bash (i prawdopodobnie większość innych powłok). Oznacza to, że bash nawet nie przeszuka ścieżki podczas szukania funkcji.

Na marginesie stanowczo odradzam dodawanie .do ścieżki ze względów bezpieczeństwa. Wyobraź sobie na przykład, cdże /tmppo dodaniu pliku wykonywalnego przez innego użytkownika /tmp/cd.

klimpergeist
źródło
2
Tak, to głupi pomysł, że mój nauczyciel zmusza nas do tego, aby „nie wyglądać nieprofesjonalnie podczas prezentacji programu”. Ocenia cię, jeśli nie jest zrobione.
Ganea Dan Andrei
1
Punkty Brownie dla wielkich nauczycieli :(
klimpergeist
4
cdjest wbudowaną powłoką, więc przykład nie będzie działał z tego samego powodu co z set.
Emil Jeřábek
15
Używanie ./foodo wywoływania programu jest profesjonalne; pokazuje, że rozumiesz, dlaczego .nie powinno być w $ PATH. Twój nauczyciel się myli i możesz mu powiedzieć, że tak powiedziałem.
zwolnienie
10

setto nie tylko wbudowane, to specjalne wbudowane POSIX . Istnieje kilka wbudowanych poleceń, które są określone w standardach, które można znaleźć w wyszukiwaniu poleceń, zanim cokolwiek innego - $PATHnie jest przeszukiwane, nazwy funkcji nie są przeszukiwane itp. Większość wbudowanych, które niespecjalne, są w rzeczywistości wymagane przez standard POSIX znalezione w tobie, $PATH zanim powłoka uruchomi dowolną z wbudowanych procedur. To jest prawda echoi większość innych (choć czy średnia jest honorowana w tym względzie była kwestią sporną w listach dyskusyjnych Open Group w przeszłości) , ale nie z set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Lub exec.

Wszystkie są zastrzeżonymi nazwami powłoki i mają specjalne atrybuty inne niż ich kolejność w wyszukiwaniu poleceń. Na przykład nie można zdefiniować funkcji powłoki o żadnej z tych nazw w powłoce zgodnej ze standardami. To dobra rzecz - pozwala ludziom bezpiecznie pisać przenośne skrypty . Są to podstawowe polecenia, dzięki którym doświadczony pisarz skryptów może ustanowić bezpieczną i niezawodną bazę w swoim środowisku. Zaatakowanie tego obszaru nazw nie jest wskazane.

Jeśli jednak chcesz go zaatakować, możesz to zrobić przenośnie alias. Kolejność rozszerzania powłoki umożliwia obejście tego problemu. Ponieważ aliasrozszerzenie jest rozwijane podczas odczytywania polecenia, cokolwiek zastąpi setnazwę w definicji, rozwinie się poprawnie, po prostu prawdopodobnie nie powinno rozwinąć się do jednej z tych nazw.

Więc możesz zrobić:

alias set=./set

... które będą działać dobrze.

mikeserv
źródło
3

Problem polega na tym, że setjest to wbudowana powłoka, a najlepszym rozwiązaniem byłoby użycie innej nazwy dla programu wykonywalnego.

Nawiasem mówiąc, w zeszłym tygodniu zadałem pytanie, jak uruchamiać polecenia systemowe zamiast wbudowanych powłok o tej samej nazwie, a rozwiązaniem, które zaakceptowałem, było uruchomienie polecenia przez env:

env set 2 3

W tym konkretnym przypadku, gdy już wiesz, że polecenie, którego chcesz użyć, znajduje się w twoim bieżącym katalogu, lepiej byłoby bezpośrednio uruchomić plik wykonywalny, wprowadzając jego ścieżkę (używając .do reprezentowania bieżącego katalogu roboczego):

./set 2 3

Oba powyższe rozwiązania są niezależne od powłoki, tzn. Będą działać niezależnie od używanej powłoki.

Sugestie takie jak użycie commandwbudowanego nie działają w Bash: to tylko zapobiega uruchomieniu funkcji powłoki . Chociaż nie jest to udokumentowane, zauważyłem również, że użycie commandrównież tłumi słowa kluczowe powłoki . Jednak nie zrobi tego samego dla wbudowanych powłok, takich jak set. Jak rozumiem, commandmoże współpracować z innymi powłokami, takimi jak zsh.

Również sztuczek takich jak \setlub "set"czy 'set'nie pracują za Bash builtins - choć są one przydatne do uruchamiania plików wykonywalnych zamiast aliasów lub skorupą słów kluczowych .

Uwaga: Ta odpowiedź początkowo była komentarzem do (zaakceptowanej) odpowiedzi Erica, ale stała się zbyt duża, aby zmieścić się w komentarzu. Inne odpowiedzi zalecające użycie ŚCIEŻKI typei nie dodawanie jej .do PATH są dobre.

Anthony Geoghegan
źródło