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 em etc / bash.bashrc assim:

PATH=.:$PATH

Eu salvei este programa como set.c e estou compilando com

gcc -o set set.c

na pasta

~/Programming/so

No entanto, quando eu chamo

set 2 3

nada acontece. Não há texto que aparece.

Chamando

./set 2 3

fornece o resultado esperado

Nunca tive um problema com o PATH antes e

which set

retorna ./set . Então parece que o PATH é o correto. O que está acontecendo?

    
por Ganea Dan Andrei 24.11.2015 / 15:36

4 respostas

24

Em vez de usar which , que não trabalhe quando precisar mais , use type para 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 buildins antes de pesquisar o $PATH , então definir $PATH nã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 em bash , mas outros shells como ksh podem não permitir. Veja a resposta do mikeserv para uma solução mais portátil.)

Agora, digitar set executará a função chamada "set", que executa ./set . O GNU bash procura por funções antes de procurar por builtins, e procura por builtins antes de pesquisar o $PATH . A seção chamada "COMMAND EXECUTION" na página man bash fornece mais informações sobre isso.

Veja também a documentação sobre builtin e command : help builtin e help command .

    
por 24.11.2015 / 17:26
11

set é um bash embutido (e provavelmente a maioria dos outros shells). Isso significa que o bash nem vai procurar o caminho ao procurar pela função.

Como observação secundária, aconselho seriamente que você adicione . ao caminho por motivos de segurança. Imagine por exemplo cd ing de /tmp depois que qualquer outro usuário adicionou um arquivo executável /tmp/cd .

    
por 24.11.2015 / 15:41
10

set não é apenas um builtin, é um especial embutido em POSIX . Existem alguns comandos embutidos que são especificados pelos padrões para serem encontrados em uma pesquisa de comando antes de qualquer outra coisa - $PATH não é pesquisado, nomes de funções não são pesquisados, etc. A maioria dos builtins que não são especial são requeridos pelo padrão POSIX encontrado em seu $PATH o shell executará qualquer um dos seus próprios procedimentos internos. Isto é verdade para echo e a maioria dos outros (embora o padrão seja honrado a esse respeito tem sido uma questão de contenção nas listas de discussão de grupos abertos 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 eles possuem atributos especiais além de sua ordem de preferência para a pesquisa de comandos também. 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 é uma coisa boa - permite que as pessoas escrevam scripts portáveis com segurança . Esses são comandos básicos a partir dos quais um roteirista experiente pode estabelecer uma posição segura e confiável em seu ambiente. Invadir este espaço de nomes é não aconselhável.

No entanto, se você desejar invadir, poderá fazê-lo portavelmente com alias . A ordem do shell expansão permite esta solução alternativa. Como o alias é expandido enquanto o comando está sendo lido, o que quer que você substitua o set name em sua definição será expandido corretamente, ele provavelmente não deve ser expandido para um desses nomes.

Então você poderia fazer:

alias set=./set

... que funcionará bem.

    
por 25.11.2015 / 13:47
3

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

Por acaso, na semana passada, fiz uma pergunta sobre como executa comandos do sistema ao invés de embutir shell com o mesmo nome e a solução que eu aceitei foi executar o comando através de env :

env set 2 3

Para este caso em particular, onde você já sabe que o comando que deseja usar está localizado em seu diretório atual, seria melhor executar diretamente o executável inserindo seu caminho (usando . para representar o diretório de trabalho atual ):

./set 2 3

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

Sugestões como usar o command incorporado não funcionarão no Bash: isso só impede que as funções do shell sejam executadas. Embora não esteja documentado, também percebi que usar command também suprime as palavras-chave do shell . No entanto, ele não fará o mesmo com os recursos internos do shell, como set . Pelo que entendi, command pode funcionar com outras shells como zsh.

Além disso, truques como \set ou "set" ou 'set' não funcionam para Bash builtins - embora sejam úteis para executar arquivos executáveis em vez de aliases ou shell palavras-chave .

Observação: essa resposta começou originalmente como um comentário sobre a resposta (aceita) de Eric, mas ficou grande demais para caber em um comentário. As outras respostas recomendando o uso de type e não adicionando . ao PATH são boas.

    
por 24.11.2015 / 22:33