¿Por qué mi programa llamado "set" no se ejecuta?

10

He creado un programa C simple así:

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

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

Y tengo mi RUTA modificada en etc / bash.bashrc así:

PATH=.:$PATH

He guardado este programa como set.c y lo estoy compilando con

gcc -o set set.c

en la carpeta

~/Programming/so

Sin embargo, cuando llamo

set 2 3

no pasa nada. No hay texto que aparezca.

Vocación

./set 2 3

da el resultado esperado

Nunca he tenido un problema con PATH antes y

which set

vuelve ./set. Entonces parece que la RUTA es la correcta. ¿Qué está pasando?

Ganea Dan Andrei
fuente
10
Es relativamente peligroso agregar '.' a tu camino. Es mejor usar ./ ​​al ejecutar algo desde el directorio local, o mover el ejecutable a un directorio conocido como ~ / bin /
TREE
77
También es una mala idea llamar a su programa de prueba testbásicamente por la misma razón; testTambién es un shell incorporado.
Jonathan Leffler
@JonathanLeffler Y sin embargo, para las pruebas rápidas, llamar a un programa testparece tener sentido. Por supuesto, para cuando lo coloques en tu PATHrealmente deberías haber encontrado un nombre diferente. Y hasta que ponga el programa en su PATHcuenta, deberá invocarlo como de ./testtodos modos. Por lo tanto, está bien usar el nombre testde un programa siempre que sea una prueba rápida que intente eliminar antes de que finalice el día.
kasperd
1
@kasperd: Que yo sepa, el nombre convencional para un programa de prueba rápida es foo.
hmakholm dejó a Mónica el
Si lo nombra, lscada vez que vaya a ver si existe, se ejecutará (pero solo si modifica su ruta como lo hizo en la pregunta).
ctrl-alt-delor

Respuestas:

24

En lugar de usar which, que no funciona cuando más lo necesita , use typepara determinar qué se ejecutará cuando escriba un comando:

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

El shell siempre busca incorporaciones antes de buscar $PATH, por lo que la configuración $PATHno ayuda aquí.

Sería mejor cambiar el nombre de su ejecutable a otra cosa, pero si su asignación requiere que se nombre el programa set, puede usar una función de shell:

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

(Eso funciona bash, pero otros shells como kshpueden no permitirlo. Vea la respuesta de mikeserv para una solución más portátil).

Ahora escribiendo setse ejecutará la función llamada "set", que se ejecuta ./set. GNU bashbusca funciones antes de buscar builtins, y busca builtins antes de buscar el $PATH. La sección denominada "EJECUCIÓN DE MANDOS" en la página del manual de bash brinda más información al respecto.

Consulte también la documentación sobre builtiny command: help builtiny help command.

yellowantphil
fuente
3
Recomiendas typemás which, pero no des ninguna razón por qué. ( Sé por qué , pero alguien que necesita la recomendación no lo haría.)
cjm
1
@cjm Aquí hay un tratado completo sobre por qué no cuál : unix.stackexchange.com/questions/85249/…
Anthony Geoghegan
Muy informativo. Nunca adivinarías que hay tanta controversia sobre un comando tan aparentemente simple que hace una tarea simple
Ganea Dan Andrei
44
@GaneaDanAndrei Principalmente, use en typelugar de which, no asigne un nombre a su programa "set", y comprenda que function set { ./set; }es un truco feo que probablemente debería evitar.
yellowantphil
11

setes un incorporado en bash (y probablemente en la mayoría de las otras conchas). Esto significa que bash ni siquiera buscará la ruta cuando busque la función.

Como comentario adicional, recomendaría encarecidamente no agregar .al camino por razones de seguridad. Imagine, por ejemplo cd, salir /tmpdespués de que cualquier otro usuario agregue un archivo ejecutable /tmp/cd.

klimpergeist
fuente
2
Sí, es una estúpida idea que mi maestro nos obligue a "no parecer poco profesional mientras presenta un programa". Te califica si no se hace.
Ganea Dan Andrei
1
Brownie apunta a grandes maestros :(
klimpergeist
44
cdes un shell incorporado, por lo que el ejemplo no funcionará por la misma razón que con set.
Emil Jeřábek
15
Usar ./foopara invocar un programa es profesional; muestra que entiendes por .qué no debería estar en $ PATH. Tu maestro está equivocado, y puedes decirle que lo dije.
zwol
10

setno es solo una construcción integrada, es una construcción especial POSIX . Hay algunas órdenes internas que son estándares especificados que se encuentran en una búsqueda de comandos antes de cualquier otra cosa - $PATHno se busca, nombres de funciones no se buscan, y etc. La mayoría de las órdenes internas que son no especiales están realmente requerido por el estándar POSIX sean encontrado en su $PATH antes de que el shell ejecute cualquiera de sus propios procedimientos integrados. Este es el caso de echoy la mayoría de los demás (aunque si la norma es honrado en este sentido ha sido un tema de discusión en las listas de correo de grupo abierto en el pasado) , pero no de set, trap, break,return, continue, ., :, times, eval, exit, export, readonly, unset, O exec.

Todos estos son nombres reservados del shell, y también tienen atributos especiales además de su orden de preferencia para la búsqueda de comandos. Por ejemplo, no puede definir una función de shell con ninguno de esos nombres en un shell compatible con los estándares. Esto es algo bueno : permite a las personas escribir scripts portátiles de forma segura . Estos son comandos de línea de base desde los cuales un escritor de guiones experimentado puede establecer un punto de apoyo seguro y confiable en su entorno. Invadir este espacio de nombres no es aconsejable.

Sin embargo, si desea invadirlo, puede hacerlo de forma portátil alias. El orden de expansión de shell permite esta solución. Debido a que aliasse expande mientras se lee el comando, lo que sea que reemplace el setnombre en su definición se expandirá correctamente, probablemente no debería expandirse a uno de esos nombres.

Entonces podrías hacer:

alias set=./set

... que funcionará bien.

mikeserv
fuente
3

El problema es que setes un shell integrado y la mejor solución sería usar un nombre diferente para su programa ejecutable.

Por cierto, la semana pasada, hice una pregunta sobre cómo ejecutar los comandos del sistema en lugar de los shell incorporados con el mismo nombre y la solución que acepté fue ejecutar el comando a través de env:

env set 2 3

Para este caso en particular, donde ya sabe que el comando que desea usar se encuentra en su directorio actual, sería mejor ejecutar directamente el ejecutable ingresando su ruta (usando .para representar el directorio de trabajo actual):

./set 2 3

Ambas soluciones anteriores son independientes de la shell, es decir, funcionarán independientemente de la shell que esté utilizando.

Sugerencias como el uso de la función commandintegrada no funcionarán en Bash: esto solo evita que se ejecuten las funciones de shell . Si bien no está documentado, también he notado que el uso commandtambién suprime las palabras clave de shell . Sin embargo, no hará lo mismo para las construcciones de shell como set. Según tengo entendido, commandpuede funcionar con otros shells como zsh.

Además, trucos tales como \seto "set", o 'set'no lo hacen el trabajo de órdenes internas del golpe - a pesar de que son útiles para la ejecución de archivos ejecutables en lugar de alias o de concha palabras clave .

Nota: Esta respuesta originalmente comenzó como un comentario sobre la respuesta (aceptada) de Eric, pero se hizo demasiado grande para caber en un comentario. Las otras respuestas que recomiendan usar typey no agregar .a la RUTA son buenas.

Anthony Geoghegan
fuente