Pourquoi mon programme appelé «set» n'est-il pas exécuté?

10

J'ai créé un programme C simple comme ceci:

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

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

Et j'ai mon PATH modifié dans etc / bash.bashrc comme ceci:

PATH=.:$PATH

J'ai enregistré ce programme en tant que set.c et je le compile avec

gcc -o set set.c

dans le dossier

~/Programming/so

Cependant, quand j'appelle

set 2 3

il ne se passe rien. Aucun texte n'apparaît.

Appel

./set 2 3

donne le résultat attendu

Je n'ai jamais eu de problème avec PATH auparavant et

which set

retourne ./set. Il semble donc que le CHEMIN soit le bon. Que se passe-t-il?

Ganea Dan Andrei
la source
10
Il est relativement dangereux d'ajouter "." à votre CHEMIN. Mieux vaut simplement utiliser ./ lors de l'exécution de quelque chose à partir du répertoire local, ou déplacer l'exécutable dans un répertoire bien connu comme ~ / bin /
TREE
7
C'est aussi une mauvaise idée d'appeler votre programme de test testpour essentiellement la même raison; testest également un shell intégré.
Jonathan Leffler
@JonathanLeffler Et pourtant, pour des tests rapides, appeler un programme testsemble logique. Bien sûr, au moment où vous le mettez dans votre, PATHvous devriez vraiment avoir trouvé un nom différent. Et jusqu'à ce que vous mettiez le programme dans votre, PATHvous devrez l'invoquer comme de ./testtoute façon. Il est donc testassez correct d'utiliser le nom d'un programme tant qu'il s'agit d'un test rapide que vous avez l'intention de supprimer avant la fin de la journée.
kasperd
1
@kasperd: Pour autant que je sache, le nom conventionnel d'un programme de test rapide est foo.
hmakholm a quitté Monica
Si vous le nommez, lschaque fois que vous allez voir s'il existe, il s'exécutera (mais uniquement si vous modifiez votre chemin comme vous l'avez fait dans la question).
ctrl-alt-delor

Réponses:

24

Au lieu d'utiliser which, qui ne fonctionne pas lorsque vous en avez le plus besoin , utilisez typepour déterminer ce qui va s'exécuter lorsque vous tapez une commande:

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

Le shell recherche toujours les commandes internes avant de rechercher le $PATH, donc le réglage $PATHn'aide pas ici.

Il serait préférable de renommer votre exécutable en quelque chose d'autre, mais si votre affectation nécessite que le programme soit nommé set, vous pouvez utiliser une fonction shell:

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

(Cela fonctionne bash, mais d'autres shells comme kshne le permettent pas. Voir la réponse de mikeserv pour une solution plus portable.)

Maintenant, la frappe setexécutera la fonction nommée "set", qui s'exécute ./set. GNU bashrecherche des fonctions avant de rechercher des commandes internes, et il recherche des commandes internes avant de rechercher le $PATH. La section nommée "EXÉCUTION DE COMMANDE" dans la page de manuel bash donne plus d'informations à ce sujet.

Voir aussi la documentation sur builtinet command: help builtinet help command.

yellowantphil
la source
3
Vous recommandez typeplus which, mais n'en donnez aucune raison. ( Je sais pourquoi , mais quelqu'un qui a besoin de la recommandation ne le ferait pas.)
cjm
1
@cjm Voici un traité sur pourquoi pas lequel : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan
Très instructif. Vous ne devineriez jamais qu'il y a autant de controverse sur une commande aussi simple en apparence qui accomplit une tâche simple
Ganea Dan Andrei
4
@GaneaDanAndrei Principalement, utilisez à la typeplace de which, ne nommez pas votre programme "set", et réalisez que function set { ./set; }c'est un vilain hack que vous devriez probablement éviter.
yellowantphil
11

setest une fonction intégrée dans bash (et probablement la plupart des autres coquilles). Cela signifie que bash ne cherchera même pas le chemin lors de la recherche de la fonction.

Comme remarque secondaire, je déconseille fortement d'ajouter .au chemin pour des raisons de sécurité. Imaginez, par exemple cd, /tmpqu'après qu'un autre utilisateur ait ajouté un fichier exécutable /tmp/cd.

klimpergeist
la source
2
Oui, c'est une idée stupide que mon professeur nous impose afin de ne pas paraître non professionnel tout en présentant un programme ". Il vous note si ce n'est pas fait.
Ganea Dan Andrei
1
Points de brownie pour de grands professeurs :(
klimpergeist
4
cdest un shell intégré, donc l'exemple ne fonctionnera pas pour la même raison qu'avec set.
Emil Jeřábek
15
Utiliser ./foopour invoquer un programme est professionnel; cela montre que vous comprenez pourquoi .ne pas être dans $ PATH. Votre professeur a tort et vous pouvez lui dire que je l'ai dit.
zwol
10

setn'est pas seulement une fonction intégrée, c'est une fonction spéciale POSIX . Il y a quelques commandes prédéfinies qui sont des normes spécifiées à trouver dans une recherche de commande avant toute autre chose - $PATH, les noms de fonctions ne sont pas recherchées ne sont pas fouillés, etc La plupart des fonctions intégrées qui sont pas spéciaux sont effectivement nécessaires par la norme POSIX pour être trouvé dans votre $PATH avant que le shell n'exécute l'une de ses propres procédures internes. Ceci est vrai echoet la plupart des autres (bien que si la norme est honorée à cet égard a été une question de discorde dans les listes de diffusion de groupe ouvert dans le passé) , mais pas de set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unsetOu exec.

Tous ces noms sont réservés au shell, et ils ont également des attributs spéciaux autres que leur ordre de préférence pour la recherche de commandes. Par exemple, vous ne pouvez pas définir une fonction shell avec l'un de ces noms dans un shell conforme aux normes. C'est une bonne chose - cela permet aux gens d'écrire des scripts portables en toute sécurité . Ce sont des commandes de base à partir desquelles un scénariste expérimenté peut établir une position sûre et fiable dans son environnement. Il n'est pas conseillé d' envahir cet espace de noms .

Cependant, si vous souhaitez l'envahir, vous pouvez le faire de manière portable avec alias. L'ordre d' expansion du shell permet ce contournement. Étant donné que le aliasest développé pendant la lecture de la commande, tout ce que vous remplacez par le setnom dans votre définition se développera correctement, il ne devrait probablement pas s'étendre à l'un de ces noms.

Vous pourriez donc faire:

alias set=./set

... qui fonctionnera très bien.

mikeserv
la source
3

Le problème est qu'il sets'agit d'un shell intégré et la meilleure solution serait d' utiliser un nom différent pour votre programme exécutable.

Soit dit en passant , la semaine dernière, j'ai posé une question sur la façon d' exécuter des commandes système au lieu des commandes internes du même nom et la solution que j'ai acceptée a été d'exécuter la commande via env:

env set 2 3

Pour ce cas particulier, où vous savez déjà que la commande que vous souhaitez utiliser se trouve dans votre répertoire courant, il serait préférable d'exécuter directement l'exécutable en entrant son chemin (en utilisant .pour représenter le répertoire de travail courant):

./set 2 3

Les deux solutions ci-dessus sont indépendantes du shell, c'est-à-dire qu'elles fonctionneront quel que soit le shell que vous utilisez.

Des suggestions telles que l'utilisation de la fonction commandintégrée ne fonctionneront pas dans Bash: cela empêche uniquement l' exécution des fonctions shell . Bien que cela ne soit pas documenté, j'ai également remarqué que l'utilisation commandsupprime également les mots clés du shell . Cependant, cela ne fera pas de même pour les commandes internes telles que set. Si je comprends bien, commandpeut fonctionner avec d'autres shells tels que zsh.

De plus, des astuces telles que \setou "set"ou 'set'ne fonctionnent pas pour les builds Bash - bien qu'elles soient utiles pour exécuter des fichiers exécutables au lieu d' alias ou de mots clés shell .

Remarque: Cette réponse a commencé à l'origine comme un commentaire sur la réponse (acceptée) d'Eric, mais elle est devenue trop grande pour tenir dans un commentaire. Les autres réponses recommandant d'utiliser typeet de ne pas ajouter .au CHEMIN sont bonnes.

Anthony Geoghegan
la source