Se eu fosse adivinhar, seu sh favorece a simplicidade ou o desempenho em relação à facilidade de uso. O erro "permissão negada" é fornecido por perror(3) , uma função padrão para imprimir uma mensagem de erro. Por exemplo:
$ cat foo.c
#include <stdio.h>
#include <unistd.h>
int main()
{
char* const args[] = { "/usr", NULL };
if (execv(args[0], args))
perror(args[0]);
return 0;
}
$ gcc -o foo foo.c
$ ./foo
/usr: Permission denied
bash provavelmente faz uma verificação para ver se o caminho é um diretório. Isso, claro, será um pouco mais lento e um código um pouco mais longo.
bash , zsh , etc. têm mais de um motivo para fazer a verificação - eles permitem que você "execute" um caminho para um diretório para cd :
$ shopt -s autocd
$ cd /
$ pwd
/
$ /usr/share
cd /usr/share
$ pwd
/usr/share
No caso de dash ( /bin/sh do Debian aponta para /bin/dash ), esse é o caso. O código que executa o comando está em shellexec() :
if (strchr(argv[0], '/') != NULL) {
tryexec(argv[0], argv, envp);
e = errno;
} else {
// snip
exerror(EXEXIT, "%s: %s", argv[0], errmsg(e, E_EXEC));
Essa função chama errms() :
const char *
errmsg(int e, int action)
{
if (e != ENOENT && e != ENOTDIR)
return strerror(e);
if (action & E_OPEN)
return "No such file";
else if (action & E_CREAT)
return "Directory nonexistent";
else
return "not found";
}
strerror(3) é outra função padrão, como perror . strerror retorna a mensagem de erro, perror imprime diretamente.