Perché il mio programma chiamato "set" non viene eseguito?


10

Ho creato un semplice programma C in questo modo:

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

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

E ho il mio PERCORSO modificato in etc / bash.bashrc in questo modo:

PATH=.:$PATH

Ho salvato questo programma come set.c e lo sto compilando con

gcc -o set set.c

nella cartella

~/Programming/so

Tuttavia, quando chiamo

set 2 3

non succede nulla. Non è presente alcun testo.

chiamata

./set 2 3

dà il risultato atteso

Non ho mai avuto problemi con PATH prima e

which set

ritorna ./set. Quindi sembra che il PERCORSO sia quello corretto. Che sta succedendo?


10
È relativamente pericoloso aggiungere "." al tuo PERCORSO. Meglio usare solo ./ quando si esegue qualcosa dalla directory locale o spostare l'eseguibile in una directory ben nota come ~ / bin /
TREE

7
È anche una cattiva idea chiamare il programma di test testper lo stesso motivo; testè anche una shell integrata.
Jonathan Leffler,

@JonathanLeffler Eppure per test rapidi chiamare un programma testsembra avere senso. Certo, quando l'avrai inserito, PATHavresti dovuto trovare un nome diverso. E fino a quando non inserirai il programma nel tuo PATHdovresti invocarlo come ./testcomunque. Quindi è un po 'OK usare il nome testper un programma purché si tratti di un test rapido che si intende eliminare prima della fine della giornata.
Kasperd,

1
@kasperd: Per quanto ne so, il nome convenzionale per un programma di test rapido è foo.
Hmakholm ha lasciato Monica il

Se lo chiami lsallora ogni volta che vai a vedere se esiste, verrà eseguito (ma solo se modifichi il tuo percorso come hai fatto nella domanda).
ctrl-alt-delor,

Risposte:


24

Invece di usare which, che non funziona quando ne hai più bisogno , usa typeper determinare cosa verrà eseguito quando digiti un comando:

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

La shell cerca sempre i builtin prima di cercare $PATH, quindi l'impostazione $PATHnon aiuta qui.

Sarebbe meglio rinominare il tuo eseguibile con qualcos'altro, ma se il tuo compito richiede che il programma sia chiamato set, puoi usare una funzione shell:

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

(Funziona bene bash, ma altri kshtipi di shell potrebbero non permetterlo. Vedi la risposta di mikeserv per una soluzione più portatile.)

Ora digitando setverrà eseguita la funzione denominata "set", che viene eseguita ./set. GNU bashcerca le funzioni prima di cercare i builtin e cerca i builtin prima di cercare $PATH. La sezione chiamata "ESECUZIONE DEI COMANDI" nella pagina man di bash fornisce maggiori informazioni al riguardo.

Vedi anche la documentazione su builtine command: help builtine help command.


3
Si consiglia di typesopra which, ma non si dà alcuna ragione per cui. ( So perché , ma qualcuno che ha bisogno della raccomandazione non lo farebbe.)
cjm

1
@cjm Ecco un intero trattato sul perché non quale : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan,

Molto informativo. Non indovineresti mai che ci sono così tante controversie su un comando così apparentemente semplice che fa un semplice compito
Ganea Dan Andrei,

4
@GaneaDanAndrei Principalmente, usa typeinvece di which, non nominare il tuo programma "set", e renditi conto che function set { ./set; }è un brutto hack che dovresti probabilmente evitare.
Yellowantphil,

11

setè un builtin in bash (e probabilmente la maggior parte delle altre shell). Ciò significa che bash non cercherà nemmeno il percorso quando cerca la funzione.

Come osservazione laterale, vorrei fortemente sconsigliare di aggiungere .al percorso per motivi di sicurezza. Immagina ad esempio di cduscire /tmpdopo che un altro utente ha aggiunto un file eseguibile /tmp/cd.


2
Sì, è una stupida idea che il mio insegnante ci imponga di non sembrare non professionale mentre presenta un programma ". Ti dà una valutazione se non è fatto.
Ganea Dan Andrei,

1
Brownie indica ottimi insegnanti :(
klimpergeist,

4
cdè una shell integrata, quindi l'esempio non funzionerà per lo stesso motivo di con set.
Emil Jeřábek,

15
L'uso ./fooper invocare un programma è professionale; mostra che capisci perché .non dovrebbe essere in $ PATH. Il tuo insegnante ha torto e potresti dirgli che l'ho detto.
zwol,

10

setnon è solo un builtin, è un builtin speciale POSIX . Ci sono alcuni comandi integrati che sono specificati come standard da trovare in una ricerca comandi prima di ogni altra cosa - $PATHnon viene cercata, i nomi delle funzioni non vengono cercati, ecc. La maggior parte dei builtin che non sono speciali sono effettivamente richiesti dallo standard POSIX per essere trovato nel tuo $PATH prima che la shell eseguirà una delle sue procedure integrate. Questo è vero echoe la maggior parte delle altre (anche se se la norma è onorata in questo senso è stata una questione di contesa alle mailing list aperta di gruppo in passato) , ma non è di set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, O exec.

Tutti questi sono nomi riservati della shell e hanno attributi speciali diversi dal loro ordine di preferenza anche per la ricerca dei comandi. Ad esempio, non è possibile definire una funzione di shell con nessuno di quei nomi in una shell conforme agli standard. Questa è una buona cosa : consente alle persone di scrivere script portatili in modo sicuro . Questi sono comandi di base dai quali uno sceneggiatore esperto può stabilire un punto d'appoggio sicuro e affidabile nel suo ambiente. Non è consigliabile invadere questo spazio dei nomi .

Tuttavia, se desideri invaderlo, puoi farlo in modo portabile alias. L'ordine di espansione della shell consente questa soluzione. Dato che aliasviene espanso mentre il comando viene letto, qualunque cosa tu sostituisca il setnome nella tua definizione si espanderà correttamente, probabilmente non dovrebbe espandersi in uno di quei nomi.

Quindi potresti fare:

alias set=./set

... che funzionerà bene.


3

Il problema è che setè incorporato nella shell e la soluzione migliore sarebbe quella di utilizzare un nome diverso per il programma eseguibile.

Per inciso, la scorsa settimana, ho posto una domanda su come eseguire i comandi di sistema invece dei builtin della shell con lo stesso nome e la soluzione che ho accettato è stata quella di eseguire il comando attraverso env:

env set 2 3

Per questo caso particolare, dove sai già che il comando che vuoi usare si trova nella tua directory corrente, sarebbe meglio eseguire direttamente l'eseguibile inserendone il percorso (usando .per rappresentare la directory di lavoro corrente):

./set 2 3

Entrambe le soluzioni sopra sono indipendenti dalla shell, cioè funzioneranno indipendentemente dalla shell che stai usando.

Suggerimenti come l'uso del commandbuiltin non funzioneranno in Bash: questo impedisce solo l' esecuzione delle funzioni della shell . Mentre, non è documentato, ho anche notato che l'uso commandsopprime anche le parole chiave della shell . Tuttavia, non farà lo stesso per i builtin della shell come set. A quanto ho capito, commandpotrebbe funzionare con altre shell come zsh.

Inoltre, trucchi, come \seto "set"o 'set'non funzionano per builtin Bash - anche se sono utili per l'esecuzione di file eseguibili invece di alias o shell parole chiave .

Nota: questa risposta originariamente è iniziata come commento sulla risposta (accettata) di Eric, ma è diventata troppo grande per adattarsi a un commento. Le altre risposte che raccomandano l'uso typee non l'aggiunta .al PERCORSO sono buone.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.