Digamos si escribí un programa con la siguiente línea:
int main(int argc, char** argv)
Ahora sabe qué argumentos de la línea de comando se le pasan comprobando el contenido de argv
.
¿Puede el programa detectar cuántos espacios entre argumentos? Como cuando escribo estos en bash:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
El entorno es un Linux moderno (como Ubuntu 16.04), pero supongo que la respuesta debería aplicarse a cualquier sistema compatible con POSIX.
Respuestas:
No tiene sentido hablar de "espacios entre argumentos"; Ese es un concepto de concha.
El trabajo de un shell es tomar líneas enteras de entrada y formarlas en matrices de argumentos para iniciar comandos. Esto puede implicar analizar cadenas entre comillas, expandir variables, comodines de archivo y expresiones tilde, y más. El comando se inicia con una
exec
llamada al sistema estándar , que acepta un vector de cadenas.Existen otras formas de crear un vector de cadenas. Muchos programas bifurcan y ejecutan sus propios subprocesos con invocaciones de comando predeterminadas, en cuyo caso, nunca existe una "línea de comando". De manera similar, un shell gráfico (de escritorio) puede iniciar un proceso cuando un usuario arrastra un icono de archivo y lo coloca en un widget de comando; nuevamente, no hay una línea de texto para tener caracteres "entre" argumentos.
En lo que respecta al comando invocado, lo que sucede en un shell u otro proceso padre / precursor es privado y oculto: solo vemos la matriz de cadenas que el estándar C especifica que
main()
puede aceptar.fuente
tar cf texts.tar *.txt
, el programa tar obtiene dos argumentos y tiene que expandir el segundo (*.txt
). Muchas personas no se dan cuenta de cómo funciona realmente hasta que comienzan a escribir sus propios scripts / programas que manejan argumentos.En general, no. El análisis de la línea de comando lo realiza el shell que no hace que la línea no analizada esté disponible para el programa llamado. De hecho, su programa podría ejecutarse desde otro programa que creó el argumento no analizando una cadena sino construyendo una matriz de argumentos mediante programación.
fuente
execve(2)
.No, esto no es posible, a menos que los espacios sean parte de un argumento.
El comando accede a los argumentos individuales desde una matriz (de una forma u otra dependiendo del lenguaje de programación) y la línea de comando real puede guardarse en un archivo de historial (si se escribe en un mensaje interactivo en un shell que tiene archivos de historial), pero es nunca pasó al comando de ninguna forma.
Todos los comandos en Unix son finalmente ejecutados por una de las
exec()
familias de funciones. Estos toman el nombre del comando y una lista o matriz de argumentos. Ninguno de ellos toma una línea de comando como se escribe en el indicador de comandos de la shell. Lasystem()
función lo hace, pero su argumento de cadena se ejecuta más tardeexecve()
, lo que, nuevamente, toma una matriz de argumentos en lugar de una cadena de línea de comando.fuente
hello
yworld
es literalmente espacios entre los dos argumentos.hello
yworld
está literalmente suministrar el segundo de los tres argumentos.En general, no es posible, como se explicaron varias otras respuestas.
Sin embargo, los shells de Unix son programas ordinarios (y están interpretando la línea de comando y englobándola , es decir, expandiendo el comando antes de hacerlo
fork
yexecve
para ello). Vea esta explicación sobrebash
las operaciones de shell . Puede escribir su propio shell (o puede parchear un shell de software libre existente , por ejemplo, GNU bash ) y usarlo como su shell (o incluso su shell de inicio de sesión, vea passwd (5) y shells (5) ).Por ejemplo, puede hacer que su propio programa de shell coloque la línea de comando completa en alguna variable de entorno (imagine
MY_COMMAND_LINE
por ejemplo) -o use cualquier otro tipo de comunicación entre procesos para transmitir la línea de comando de shell a proceso hijo-.No entiendo por qué querrías hacer eso, pero podrías codificar un shell que se comporte de esa manera (pero recomiendo no hacerlo).
Por cierto, un programa podría ser iniciado por algún programa que no sea un shell (pero que hace fork (2) luego execve (2) , o simplemente
execve
para iniciar un programa en su proceso actual). En ese caso, no hay línea de comando en absoluto, y su programa podría iniciarse sin un comando ...Tenga en cuenta que puede tener algún sistema Linux (especializado) sin ningún shell instalado. Esto es extraño e inusual, pero posible. A continuación, tendrá que escribir una especializada init programa a partir de otros programas según sea necesario - sin utilizar ningún cáscara pero al hacerlo
fork
yexecve
las llamadas al sistema.Lea también Sistemas operativos: tres piezas fáciles y no olvide que
execve
prácticamente siempre es una llamada al sistema (en Linux, se enumeran en syscalls (2) , consulte también la introducción (2) ) que reinicializa el espacio de direcciones virtuales (y algunos otros cosas) del proceso que lo hace.fuente
argv[0]
para el nombre del programa y los elementos restantes para los argumentos son especificaciones POSIX y no se pueden cambiar.argv[-1]
Supongo que un entorno de tiempo de ejecución podría especificar para la línea de comando ...execve
documentación. No se puede usarargv[-1]
, es un comportamiento indefinido usarlo.execvepluscmd
con un parámetro adicional (o convención argv), el syscall construye un vector de argumento para main que contiene un puntero a la línea de comando antes del puntero al nombre del programa, y luego pasa la dirección del puntero al nombre del programa comoargv
cuando se llama al programamain
...sh
. Entonces no es nuevo.Siempre puede decirle a su shell que diga a las aplicaciones qué código de shell condujo a su ejecución. Por ejemplo, con
zsh
, al pasar esa información en la$SHELL_CODE
variable de entorno usando elpreexec()
gancho (printenv
usado como ejemplo, usaríagetenv("SHELL_CODE")
en su programa):Todos esos se ejecutarían
printenv
como:Permitiendo
printenv
recuperar el código zsh que condujo a la ejecución deprintenv
esos argumentos. Lo que desearía hacer con esa información no me queda claro.Con
bash
, la característica más cercana azsh
'spreexec()
sería usarla$BASH_COMMAND
en unaDEBUG
trampa, pero tenga en cuenta quebash
tiene cierto nivel de reescritura en eso (y en particular refactoriza parte del espacio en blanco utilizado como delimitador) y eso se aplica a cada comando (bueno, algunos) ejecutar, no toda la línea de comando como se ingresó en el indicador (ver también lafunctrace
opción).Vea cómo algunos de los espacios que son delimitadores en la sintaxis del lenguaje de shell se han comprimido en 1 y cómo no la línea de comando completa no siempre se pasa al comando. Probablemente no sea útil en su caso.
Tenga en cuenta que no recomendaría hacer este tipo de cosas, ya que potencialmente está filtrando información confidencial a cada comando como en:
filtraría ese secreto a ambos
wc
yuntrustedcmd
.Por supuesto, podría hacer ese tipo de cosas para otros idiomas además del shell. Por ejemplo, en C, podría usar algunas macros que exportan el código C que ejecuta un comando al entorno:
Ejemplo:
Vea cómo el preprocesador C condensó algunos espacios como en el caso bash. En la mayoría de los idiomas, si no en todos, la cantidad de espacio utilizado en los delimitadores no hace ninguna diferencia, por lo que no es sorprendente que el compilador / intérprete se tome algo de libertad aquí.
fuente
BASH_COMMAND
no contenía los argumentos de separación de espacios en blanco originales, por lo que esto no era utilizable para la solicitud literal del OP. ¿Esta respuesta incluye alguna demostración en ambos sentidos para ese caso de uso en particular?Solo agregaré lo que falta en las otras respuestas.
No
Ver otras respuestas
Tal vez
No se puede hacer nada en el programa, pero hay algo que se puede hacer en el shell cuando ejecuta el programa.
Necesitas usar comillas. Entonces en lugar de
necesitas hacer uno de estos
Esto pasará un único argumento al programa, con todos los espacios. Hay una diferencia entre los dos, el segundo es literal, exactamente la cadena como aparece (excepto que
'
debe escribirse como\'
). El primero interpretará algunos caracteres, pero se dividirá en varios argumentos. Ver cotización de shell para más información. Así que no hay necesidad de reescribir el shell, los diseñadores del shell ya lo han pensado. Sin embargo, como ahora es un argumento, tendrá que pasar más dentro del programa.opcion 2
Pase los datos a través de stdin. Esta es la forma normal de obtener grandes cantidades de datos en un comando. p.ej
o
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(Las cursivas son salida del programa)
fuente
./myprog␣"␣␣␣␣␣aaa␣␣␣␣␣␣bbb"
ejecuta (generalmente en un proceso secundario) el archivo almacenado./myprog
y le pasa dos argumentos:./myprog
y␣␣␣␣␣aaa␣␣␣␣␣␣bbb
(argv[0]
yargc[1]
,argc
siendo 2) y, como en los OP, el espacio que separa esos dos argumentos no se pasa de ninguna manera amyprog
.