Por que meu programa chamado "set" não está sendo executado?

10

Eu criei um programa C simples assim:

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

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

E eu tenho meu PATH modificado no etc / bash.bashrc da seguinte forma:

PATH=.:$PATH

Salvei este programa como set.c e estou compilando-o com

gcc -o set set.c

na pasta

~/Programming/so

No entanto, quando ligo

set 2 3

nada acontece. Não há texto que apareça.

Chamando

./set 2 3

dá o resultado esperado

Eu nunca tive um problema com PATH antes e

which set

retorna ./set. Parece que o CAMINHO é o correto. O que está acontecendo?

Ganea Dan Andrei
fonte
10
É relativamente perigoso adicionar '.' ao seu CAMINHO. É melhor usar ./ ​​ao executar algo do diretório local ou mover o executável para um diretório conhecido como ~ / bin /
TREE
7
Também é uma má idéia chamar seu programa de teste testbasicamente pelo mesmo motivo; testtambém é um shell embutido.
Jonathan Leffler
@ JonathanLeffler E ainda assim, para testes rápidos, chamar um programa testparece fazer sentido. É claro que, no momento em que você o coloca, PATHvocê deve ter um nome diferente. E até você colocar o programa no seu, PATHvocê teria que invocá-lo da mesma ./testmaneira. Portanto, é bom usar o nome testde um programa, desde que seja um teste rápido que você deseja excluir antes do final do dia.
kasperd
1
@kasperd: Até onde eu sei, o nome convencional para um programa de teste rápido é foo.
hmakholm deixou Monica em 25/11
Se você o nomear, lssempre que for ver se ele existe, ele será executado (mas somente se você modificar seu caminho, como fez na pergunta).
Ctrl-alt-delor 22/12/2015

Respostas:

24

Em vez de usar which, que não funciona quando você mais precisa , use typepara determinar o que será executado quando você digitar um comando:

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

O shell sempre procura por componentes internos antes de pesquisar no $PATH, portanto, a configuração $PATHnão ajuda aqui.

Seria melhor renomear seu executável para outra coisa, mas se sua atribuição exigir que o programa seja nomeado set, você poderá usar uma função de shell:

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

(Isso funciona bash, mas outros shells kshpodem não permitir. Consulte a resposta do mikeserv para obter uma solução mais portátil.)

Agora, a digitação setexecutará a função denominada "set", que é executada ./set. O GNU bashprocura funções antes de procurar por componentes internos e procura por componentes internos antes de pesquisar no $PATH. A seção chamada "COMMAND EXECUTION" na página de manual do bash fornece mais informações sobre isso.

Consulte também a documentação sobre builtine command: help builtine help command.

yellowantphil
fonte
3
Você recomenda typemais which, mas não dão qualquer motivo. ( Eu sei porque , mas alguém que precisa a recomendação não faria.)
CJM
1
@cjm Aqui está um tratado inteiro sobre por que não que : unix.stackexchange.com/questions/85249/...
Anthony Geoghegan
Muito informativo. Você nunca acho que não há tanta controvérsia sobre tal um comando aparentemente simples que faz uma tarefa simples
Ganea Dan Andrei
4
@GaneaDanAndrei Principalmente, use em typevez de which, não nomeie seu programa como "definido" e perceba que function set { ./set; }é um truque feio que você provavelmente deve evitar.
yellowantphil
11

seté um builtin no bash (e provavelmente na maioria das outras conchas). Isso significa que o bash nem pesquisará o caminho ao procurar a função.

Como observação lateral, eu recomendaria fortemente que não aumentasse .o caminho por razões de segurança. Imagine, por exemplo, cdsair /tmpapós qualquer outro usuário adicionar um arquivo executável /tmp/cd.

klimpergeist
fonte
2
Sim, é uma ideia estúpida que meu professor nos impõe a fim de não parecer profissional enquanto apresenta um programa ". Ele classifica você para baixo se não for feito.
Ganea Dan Andrei #:
1
Pontos de brownie para ótimos professores :(
klimpergeist
4
cdé um shell interno, portanto, o exemplo não funcionará pelo mesmo motivo que com set.
Emil Jerabek
15
Usar ./foopara invocar um programa é profissional; mostra que você entende por .que não deve estar em $ PATH. Seu professor está errado, e você pode dizer a ele que eu disse isso.
Zwol
10

setnão é apenas um embutido, é um embutido especial do POSIX . Existem alguns comandos internos que são padrões especificados para serem encontrados em uma pesquisa de comandos antes de qualquer outra coisa - $PATHnão são pesquisados, nomes de funções não são pesquisados ​​etc. A maioria dos componentes internos que não são especiais são realmente exigidos pelo padrão POSIX encontrado $PATH antes de o shell executar qualquer um de seus próprios procedimentos internos. Isto é verdade para echoe a maioria dos outros (embora se a norma é honrado a este respeito tem sido um assunto de discórdia nas listas de discussão do grupo aberto no passado) , mas não de set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, Ou exec.

Todos esses são nomes reservados do shell e possuem atributos especiais além da ordem de preferência para a pesquisa de comandos. Por exemplo, você não pode definir uma função de shell com nenhum desses nomes em um shell compatível com os padrões. Isso é bom - permite que as pessoas escrevam scripts portáteis com segurança . Esses são comandos de linha de base a partir dos quais um escritor de scripts experiente pode estabelecer uma base segura e confiável em seu ambiente. Invadir esse espaço para nome não é aconselhável.

No entanto, se você deseja invadi-lo, pode fazê-lo de maneira portável alias. A ordem da expansão do shell permite essa solução alternativa. Como o aliasé expandido enquanto o comando está sendo lido, o que quer que você substitua o setnome na sua definição será expandido corretamente, provavelmente não deve ser expandido para um desses nomes.

Então você poderia fazer:

alias set=./set

... o que vai funcionar muito bem.

mikeserv
fonte
3

O problema é que seté um shell embutido e a melhor solução seria usar um nome diferente para o seu programa executável.

Aliás, na semana passada, fiz uma pergunta sobre como executar comandos do sistema em vez de shell builtins com o mesmo nome e a solução que aceitei foi executar o comando através de env:

env set 2 3

Nesse caso específico, onde você já sabe que o comando que deseja usar está localizado no diretório atual, seria melhor executar diretamente o executável digitando seu caminho (usando .para representar o diretório de trabalho atual):

./set 2 3

As duas soluções acima são independentes de shell, ou seja, elas funcionarão independentemente do shell que você estiver usando.

Sugestões como o uso do commandbuiltin não funcionarão no Bash: isso apenas impede a execução de funções do shell . Embora não esteja documentado, também notei que o uso commandtambém suprime as palavras-chave do shell . No entanto, ele não fará o mesmo para os shell embutidos , como set. Pelo que entendi, commandpode funcionar com outros shells, como o zsh.

Além disso, truques, como \setou "set"ou 'set'não fazer o trabalho para builtins Bash - embora eles são úteis para a execução de arquivos executáveis em vez de aliases ou shell palavras-chave .

Nota: Esta resposta começou originalmente como um comentário na resposta de Eric (aceita), mas cresceu muito para caber em um comentário. As outras respostas recomendando o uso typee não adicionando .ao PATH são boas.

Anthony Geoghegan
fonte