¿Por qué argv incluye el nombre del programa?

Los programas típicos de Unix / Linux aceptan las entradas de la línea de comandos como un recuento de argumentos (int argc) y un vector de argumentos (char *argv[]). El primer elemento de argv es el nombre del programa, seguido de los argumentos reales.

¿Por qué se pasa el nombre del programa al ejecutable como argumento? ¿Hay ejemplos de programas que utilicen su propio nombre (tal vez algún tipo de exec situación)?

Comentarios

  • como mv y cp?
  • En Debian sh hay un enlace simbólico a dash. Se comportan de manera diferente, cuando se llaman como sh o como dash
  • @AlexejMagura Si usa algo como busybox (común en los discos de rescate y similares), entonces prácticamente todo (cp, mv, rm, ls, …) es un enlace simbólico a busybox.
  • Yo ‘ encuentro esto realmente difícil de ignorar, así que ‘ ll dilo: probablemente te refieres a » GNU » programas (gcc, bash, gunzip, la mayor parte del resto del sistema operativo …), ya que Linux es solo el kernel.
  • @ wizzwizz4 ¿Qué ‘ está mal con » programas típicos de Unix / Linux «? Lo leo como » Programas típicos que se ejecutan en Unix / Linux «. Eso ‘ es mucho mejor que su restricción a ciertos programas GNU. Dennis Ritchie ciertamente no estaba usando ningún programa GNU. Por cierto, el kernel Hurd es un ejemplo de un programa GNU que no tiene una función principal …

Respuesta

Para empezar, tenga en cuenta que argv[0] no es necesariamente el nombre del programa. Es lo que la persona que llama pone en argv[0] de la execve llamada al sistema (p. Ej., Consulte esta pregunta en Stack Overflow ). (Todas las demás variantes de exec no son llamadas al sistema, sino interfaces para execve.)

Supongamos, por ejemplo, lo siguiente (usando execl):

execl("/var/tmp/mybackdoor", "top", NULL); 

/var/tmp/mybackdoor es lo que se ejecuta pero argv[0] se establece en top, y esto es lo que ps o ( el real) top se mostraría. Consulte esta respuesta en U & L SE para obtener más información al respecto.

Configuración de todos Aparte de esto: antes de la llegada de sofisticados sistemas de archivos como /proc, argv[0] era la única forma de que un proceso aprendiera sobre su propio nombre. ¿Para qué sería bueno eso?

  • Varios programas personalizan su comportamiento según el nombre por el que fueron llamados (generalmente mediante enlaces simbólicos o duros, por ejemplo BusyBox «s utilities ; se proporcionan varios ejemplos más en otras respuestas a esta pregunta).
  • Además, los servicios, demonios y otros programas que inician sesión a través de syslog a menudo anteponen su nombre al entradas de registro; sin esto, el seguimiento de eventos sería prácticamente inviable.

Comentarios

  • Ejemplos de tales programas son bunzip2, bzcat y bzip2, para los cuales los dos primeros son enlaces simbólicos al tercero.
  • @Ruslan Curiosamente zcat no es un enlace simbólico. Parecen evitar las desventajas de esta técnica utilizando un script de shell en su lugar. Pero no imprimen un salida porque alguien que agregó opciones a gzip se olvidó de main tain zcat también.
  • Desde que tengo memoria, los estándares de codificación GNU han desalentado el uso de argv [0] para cambiar el comportamiento del programa ( sección » Estándares para interfaces generalmente » en la versión actual ). gunzip es una excepción histórica.
  • busybox es otro excelente ejemplo. Puede ser llamado por 308 nombres diferentes para invocar diferentes comandos: busybox.net/downloads/BusyBox.html#commands
  • Muchos, muchos más programas también inyectan su argv[0] en su salida de uso / ayuda en lugar de codificar su nombre. Algunos en su totalidad, otros solo el nombre base.

Respuesta

Mucho:

  • Bash se ejecuta en modo POSIX cuando argv[0] es sh. Se ejecuta como un shell de inicio de sesión cuando argv[0] comienza con -.
  • Vim se comporta de manera diferente cuando se ejecuta como vi, view, evim, eview, ex, vimdiff, etc.
  • Busybox, como ya se mencionó.
  • En sistemas con systemd como init, shutdown, reboot, etc. son enlaces simbólicos a systemctl .
  • y así sucesivamente.

Comentarios

  • Otro es sendmail y mail. Cada uno de los MTA de Unix viene con un enlace simbólico para esos dos comandos, y está diseñado para emular el comportamiento original de ‘ s cuando se llama como tal, lo que significa que cualquier programa Unix que necesite enviar correo sabe exactamente cómo pueden hacerlo.
  • otro caso común: test y [: cuando llamas al primero , maneja un error si el último argumento es ]. (en Debian estable, estos comandos son dos programas diferentes, pero las versiones anteriores y MacO todavía usan el mismo programa). Y tex, latex y así sucesivamente: el binario es el mismo, pero mirando cómo se llamó, elige el adecuado archivo de configuración . init es similar.
  • Relacionado, [ lo considera un error si el último argumento es no ].
  • Supongo que esto responde a la segunda pregunta, pero no a la primera. Dudo mucho que algún diseñador de SO se sentara y dijera » Oye, sería genial si tuviera el mismo programa haciendo cosas diferentes solo en función de su nombre ejecutable. Supongo que ‘ incluiré el nombre en su matriz de argumentos, luego. «
  • @Joey Sí, el la redacción pretende transmitir que (P: » ¿Hay alguna …? » A: » Mucho: … «)

Respuesta

Históricamente, argv es solo una matriz de punteros a las «palabras» de la línea de comandos, por lo que tiene sentido comenzar con la primera «palabra», que resulta ser la nombre del programa.

Y hay bastantes programas que se comportan de manera diferente según el nombre que se use para llamarlos, por lo que puede crear diferentes enlaces a ellos y obtener diferentes «comandos». El ejemplo más extremo que se me ocurre es busybox , que actúa como varias docenas de «comandos» diferentes dependiendo de cómo se llama .

Editar

: Referencias para la 1ª edición de Unix, según lo solicitado

Se puede ver p. ej. de la función principal de cc que argc y argv ya se utilizaron. El shell copia los argumentos en la parbuf dentro de la newarg parte de el bucle, mientras trata el comando en sí de la misma manera que los argumentos. (Por supuesto, luego ejecuta solo el primer argumento, que es el nombre del comando). Parece execv y los parientes no existían entonces.

Comentarios

  • por favor agregue referencias que Haga una copia de seguridad de esto.
  • De un vistazo rápido, exec toma el nombre del comando a ejecutar y una matriz terminada en cero de punteros de caracteres (se ve mejor en minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , donde exec toma referencias a la etiqueta 2 y la etiqueta 1, y en la etiqueta 2: aparece etc/init\0, y en la etiqueta 1: aparece una referencia a la etiqueta 2 y un cero final), que es básicamente lo que execve hace hoy menos envp.
  • execv y execl han existido » desde siempre » (es decir, desde principios hasta mediados de la década de 1970) – execv era una llamada al sistema y era una función de biblioteca que lo llamaba. execve no ‘ no existía entonces porque el entorno no ‘ existía entonces. Los otros miembros de la familia se agregaron más tarde.
  • @ G-Man ¿Puedes señalarme execv en la fuente v1 que vinculé? Solo por curiosidad.

Respuesta

Casos de uso:

Puede usar el nombre del programa para cambiar el comportamiento del programa .

Por ejemplo, podría crear algunos enlaces simbólicos al binario real.

Un ejemplo famoso en el que se utiliza esta técnica es el proyecto busybox que instala un solo binario y muchos enlaces simbólicos a él. (ls, cp, mv, etc.). Lo están haciendo para ahorrar espacio de almacenamiento porque sus objetivos son pequeños dispositivos integrados.

Esto también es usado en setarch de util-linux:

$ ls -l /usr/bin/ | grep setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 i386 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux32 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux64 -> setarch -rwxr-xr-x 1 root root 14680 2015-10-22 16:54 setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 x86_64 -> setarch 

Aquí están usando esta técnica básicamente para evitar muchos archivos fuente duplicados o simplemente para mantener las fuentes más legibles.

Otro caso de uso sería un programa que necesita para cargar algunos módulos o datos en tiempo de ejecución. Tener la ruta del programa le permite cargar módulos desde una ruta relativa a la ubicación del programa .

Además, muchos programas imprimen mensajes de error, incluido el nombre del programa .

Por qué :

  1. Porque es una convención POSIX ( man 3p execve):

argv es una matriz de cadenas de argumentos que se pasan al nuevo programa. Por convención, la primera de estas cadenas debe contener el nombre de archivo asociado con el archivo que se está ejecutando.

  1. It «s C estándar (al menos C99 y C11):

Si el valor de argc es mayor que cero, la cadena apuntada por argv [0 ] representa el nombre del programa; argv [0] [0] será el carácter nulo si el nombre del programa no está disponible en el entorno host.

Tenga en cuenta que el estándar C dice «programa nombre «no» nombre de archivo «.

Comentarios

  • No ‘ t esta pausa si alcanza el ¿enlace simbólico de otro enlace simbólico?
  • @Mehrdad, Sí, eso ‘ es la desventaja y puede ser confuso para el usuario.
  • @rudimeier: Tus ‘ ¿Por qué ‘ elementos no son realmente razones, ‘ son solo una » homunculus «, es decir, simplemente plantea la pregunta de por qué el estándar requiere que este sea el caso.
  • @ einpoklum OP ‘ s pregunta fue: ¿Por qué se pasa el nombre del programa al ejecutable? Respondí: Porque POSIX y el estándar C nos dicen que lo hagamos. ¿Cómo crees que ‘ no es realmente una razón ? Si los documentos que ‘ he citado no existieran, probablemente muchos programas no pasarían el nombre del programa.
  • El OP está preguntando efectivamente » ¿POR QUÉ los estándares POSIX y C dicen que se haga esto? » De acuerdo, la redacción estaba en un nivel abstracto, pero parece claro. Siendo realistas, la única forma de saberlo es preguntar a los creadores.

Responder

Además de los programas que alteran su comportamiento dependiendo de cómo fueron llamados, encuentro argv[0] útil para imprimir el uso de un programa, así:

printf("Usage: %s [arguments]\n", argv[0]); 

Esto hace que el mensaje de uso use siempre el nombre con el que fue llamado. Si se cambia el nombre del programa, su mensaje de uso cambia con él. Incluso incluye el nombre de ruta con el que se llamó:

# cat foo.c #include <stdio.h> int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); } # gcc -Wall -o foo foo.c # mv foo /usr/bin # cd /usr/bin # ln -s foo bar # foo Usage: foo [arguments] # bar Usage: bar [arguments] # ./foo Usage: ./foo [arguments] # /usr/bin/foo Usage: /usr/bin/foo [arguments] 

Es un buen toque, especialmente para pequeñas herramientas / scripts de propósito especial que pueden estar en todas partes el lugar.

Esto también parece una práctica común en las herramientas GNU, consulte ls por ejemplo:

% ls --qq ls: unrecognized option "--qq" Try "ls --help" for more information. % /bin/ls --qq /bin/ls: unrecognized option "--qq" Try "/bin/ls --help" for more information. 

Comentarios

  • +1. Iba a sugerir lo mismo. Es extraño que tanta gente se centre en cambiar el comportamiento y no mencione probablemente lo más obvio y uso mucho más extendido.

Respuesta

Uno ejecuta el programa escribiendo: program_name0 arg1 arg2 arg3 ....

Entonces, el shell ya debería dividir el token, y el primer token ya es el nombre del programa. Y por cierto, hay los mismos índices en el lado del programa y en el shell.

Creo que esto fue solo un truco de conveniencia (al principio) y, como puede ver en otras respuestas, también fue muy útil, por lo que esta tradición continuó y et como API.

Respuesta

Básicamente, argv incluye el nombre del programa para que pueda escribir mensajes de error como prgm: file: No such file or directory, que se implementaría con algo como esto:

 fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] ); 

Respuesta

Otro ejemplo de una aplicación de esto es este programa, que se reemplaza a sí mismo con … sí mismo, hasta que escribe algo que no es «t y.

#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char** argv) { (void) argc; printf("arg: %s\n", argv[1]); int count = atoi(argv[1]); if ( getchar() == "y" ) { ++count; char buf[20]; sprintf(buf, "%d", count); char* newargv[3]; newargv[0] = argv[0]; newargv[1] = buf; newargv[2] = NULL; execve(argv[0], newargv, NULL); } return count; } 

Obviamente, es un ejemplo ingenioso aunque interesante, pero creo que esto puede tener usos reales, por ejemplo, un binario que se actualiza automáticamente, que reescribe su propio espacio de memoria con una nueva versión de sí mismo que descargó o cambió.

Ejemplo:

$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $ 

Fuente y más información .

Comentarios

  • Felicitaciones por llegar a 1000.

Respuesta

La ruta al programa es argv[0], para que el programa pueda recuperar archivos de configuración, etc. de su directorio de instalación.
Esto sería imposible sin argv[0].

Comentarios

  • Eso ‘ no es una explicación particularmente buena; no hay ‘ razón por la que no podamos ‘ t se han estandarizado en algo como (char *path_to_program, char **argv, int argc) por ejemplo
  • Afaik, la mayoría de los programas extraen la configuración de una ubicación estándar (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) y tomar un parámetro para cambiarlo o tener una opción de tiempo de compilación que hornea en una constante al binario.

Answer

ccache se comporta de esta manera para imitar diferentes llamadas a los binarios del compilador. ccache es una caché de compilación; el objetivo es nunca compilar el mismo código fuente dos veces, sino devolver el código objeto desde la caché si es posible.

Desde ccache man page , «hay dos formas de usar ccache. Puede prefijar sus comandos de compilación con ccache o puede dejar que ccache se haga pasar por el compilador creando un enlace simbólico (denominado como el compilador) a ccache. El primer método es más conveniente si solo desea probar ccache o si desea usarlo para algunos proyectos específicos. El segundo método es más útil cuando desea usar ccache para todas sus compilaciones «.

El El método de enlaces simbólicos implica ejecutar estos comandos:

cp ccache /usr/local/bin/ ln -s ccache /usr/local/bin/gcc ln -s ccache /usr/local/bin/g++ ln -s ccache /usr/local/bin/cc ln -s ccache /usr/local/bin/c++ ... etc ... 

… cuyo efecto es permitir que ccache capture cualquier comando que de otro modo hubiera ido a los compiladores, permitiendo así que ccache devuelva un archivo en caché o pase el comando al compilador real.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *