Un archivo de script ejecutable que se ejecuta en POSIX y Windows

16

Reto : escriba un único archivo de script foo.cmdque se pueda invocar desde el cmd.exeindicador de Windows (no PowerShell, no en modo administrador), para ejecutar código arbitrario específico de Windows ...

> .\foo.cmd
Hello Windows! 

... pero también ser invocada inalterada desde una típica compatible con POSIX (Linux / OSX) Indicador Shell ( bash, tcsho zsh), para ejecutar código arbitrario POSIX-específica:

$ chmod a+x foo.cmd
$ ./foo.cmd
Hello POSIX!

... sin necesidad de instalar o crear intérpretes / herramientas de terceros.

Sé que esto es posible, pero con cruft (es decir, en Windows, una o dos líneas de basura / mensaje de error se imprimen en stderr o stdout antes de "¡Hola Windows!").

El criterio ganador es la minimización de (primero) el número de líneas cruft y (segundo) el número de caracteres cruft.

Cruft se puede definir como cualquier salida de consola (stdout o stderr) que no se produce mediante el código de carga útil (arbitrario). Las líneas en blanco se cuentan en el recuento de líneas. Las líneas nuevas no se cuentan en el recuento de caracteres. Los puntajes crudos deben sumarse en ambas plataformas. Hagamos caso omiso de mecanismos como clsese barren la ruina pero a costa de eliminar también la salida de terminal anterior. Si Windows repite sus comandos porque aún no lo @echo offha activado , excluyamos los caracteres que gasta al imprimir el directorio actual y la solicitud.

Un criterio secundario es la simplicidad / elegancia de la solución interna foo.cmd: si la "infraestructura" se define como cualquier carácter que no esté directamente involucrado en el código arbitrario de la carga útil, minimice primero el número de líneas que contienen caracteres de infraestructura, y segundo el número total de infraestructura caracteres.

¡Felicitaciones adicionales si la parte POSIX funcionará a pesar de que el archivo tenga finales de línea CRLF! (No estoy seguro de que la última parte sea posible).

Mi solución existente, que publicaré aquí una vez que otros hayan tenido la oportunidad, usa 6 líneas de código de infraestructura (52 caracteres excluyendo nuevas líneas). Produce 5 líneas de cruft, dos de las cuales están en blanco, todas las cuales ocurren en Windows (30 caracteres excluyendo nuevas líneas y excluyendo la cadena actual de directorio / solicitud que aparece en dos de esas líneas).

jez
fuente
tiene que ser un script de shell / lote, ¿ya?
gato
1
¿Alguien sabe de un entorno de prueba en línea para DOS?
Digital Trauma
1
Encontré este pero no me deja ingresar los caracteres ":", "\", "{" o "}": - /
Digital Trauma
@DigitalTrauma There is Wine (que debería haber instalado, si no lo hace, porque es útil)
cat
1
@cat gracias - Mi respuesta parece funcionar con vino ahora.
Digital Trauma

Respuestas:

15

0 líneas cruft, 0 caracteres cruft, 2 infra. líneas, 21 infra. caracteres, CRLF ok

:<<@goto:eof
@echo Hello Windows!
@goto:eof
echo "Hello POSIX!" #

Se eliminó la otra solución.

17 caracteres usando exit /bla respuesta de Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #
jimmy23013
fuente
Fuerte contendiente! Particularmente la segunda versión (en el criterio de simplicidad / elegancia, es mucho mejor no tener que empañar las líneas de carga útil como lo hace la primera versión). Esto funciona para mí en Windows y OSX. Inicialmente recibí una línea de mensaje que decía : command not foundcuando una línea en blanco se deslizaba en la carga útil posix, pero finalmente descubrí que no se trataba :de la primera línea, sino más bien debido a los CRLF desprotegidos #. Era una novedad para mí que #!no se necesitaba una línea, que era responsable de dos de las líneas de Windows cruft en mi versión anterior.
jez
La primera versión es difícil de calificar, ya que presumiblemente agrega los caracteres : ` >&3` \ a cada línea de la carga útil, supongo que se podría decir que su costo de infraestructura es arbitrariamente alto.
jez
@jez ¿Estás calculando una puntuación numérica para las respuestas? Si es así, debe aclarar mucho la cuestión de cómo hacerlo.
Trauma digital
Soy nuevo en codegolf, así que TBH pensé que se esperaba que obtuviera respuestas objetivas de alguna manera. No obstante, creo que la pregunta lo deja claro: primero, número de líneas crudas; romper lazos con eso por número de caracteres crudos; luego en número de líneas de infraestructura, luego número de caracteres de infraestructura. No esperaba soluciones que empañen las líneas de carga útil, como la primera solución de Jimmy. Cambiaré la pregunta un poco para dejar en claro que no es deseable (perdón por ese ligero cambio de meta pero no creo que afecte su segunda solución o ninguna versión suya hasta ahora)
jez
Parece que nuestras respuestas son convergentes ;-) Sin embargo, usted tiene la ventaja en la puntuación con el uso de heredoc. ¿Es #necesaria la final ?
Digital Trauma
4

Puntuación 0 cruft + 4 líneas infra + 32 caracteres infra. LF y CRLF OK.

Esto se basa en lo que encontré en este blog , con los bits de Amiga y otras líneas innecesarias eliminadas. Escondí las líneas de DOS en comillas comentadas en lugar de usar \líneas continuas, para que esto pueda funcionar tanto con CRLF como con LF.

@REM ()(:) #
@REM "
@ECHO Hello Windows!
@EXIT /B
@REM "
echo Hello POSIX!

Con terminaciones de línea DOS CRLF o * nix LF, funciona en Ubuntu, OSX y wine:

ubuntu@ubuntu:~$ ./dosix.bat
Hello POSIX!
ubuntu@ubuntu:~$ wine cmd.exe
Wine CMD Version 5.1.2600 (1.6.2)

Z:\home\ubuntu>dosix.bat
Hello Windows!

Z:\home\ubuntu>exit
ubuntu@ubuntu:~$ 

Para crear esto exactamente (con CRLF) en una máquina * nix (incluido OSX), pegue lo siguiente en un terminal:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF
Trauma digital
fuente
¡Se ve bien! dosixEs un buen nombre también. Pero en mi Mac (OS 10.9.4, Darwin Kernel Versión 13.3.0, GNU bash versión 3.2.51) no funciona con: ¿ ./dosix.cmd: line 13: syntax error: unexpected end of file Alguna idea de por qué?
jez
@jez, asegúrese de guardar el archivo codificado con UTF-8 y preservar las terminaciones de línea de Windows.
gato
Ah, estaba sucediendo porque sin saberlo tenía finales de línea de Windows y estaba usando la primera versión.
jez
Tenga en cuenta que puede ingresar el carácter CR en Bash insertando (o Cv), ingresando (o Cm).
jimmy23013
@jez Así que esto se puntúa como 0 líneas de cruft + 4 líneas de infraestructura + 32 caracteres de infraestructura. ¿Deberían combinarse estos números de manera significativa para crear una puntuación única para comparar?
Digital Trauma
2

Ya publicaré la solución que había estado usando, ya que ha sido superada. Es cortesía de un colega mío que creo que debe haber leído la misma entrada de blog que Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Windows cruft: 5 líneas (de las cuales dos están en blanco) / 30 caracteres
  • OSX cruft: 0
  • Infraestructura: 6 líneas / 52 caracteres
  • Compatibilidad CRLF: solo si al intérprete nombrado en la #!línea no le importa (por lo tanto, para intérpretes estándar como shy amigos, falla)
jez
fuente
En POSIX, un script siempre comienza #!, lo que significa que la suya es la única respuesta hasta ahora para ser un script válido en un sistema POSIX. Seguro que algunos de los otros pueden ejecutarse si, pero solo si se inician desde un shell con una solución alternativa para scripts defectuosos.
kasperd
¡Bueno saber! Supongo que estoy confuso con los criterios estrictos para el cumplimiento de POSIX. Mi verdadero objetivo es tener archivos de script que funcionen "fuera de la caja" en "la mayoría" de los sistemas de escritorio modernos, lo que sea que eso signifique: Windows 7/8/10, Ubuntu, OSX ... ¿Qué tan universal es la solución para los scripts shebangless, ¿Me pregunto? De todos modos, no voy a otorgarme los puntos :-)
jez
No me había dado cuenta de que tanto la respuesta como la pregunta fueron escritas por la misma persona. Creo que todos los shells con los que he trabajado tienen una solución alternativa para una #!línea faltante , pero usarán diferentes shells para interpretar el guión. Eso significa que un "script" que no comienza #!tiene que ser válido no solo en un shell, sino en cada shell que pueda ser interpretado de manera plausible. Pero aún peor, solo funciona cuando se inicia desde otro shell. Tal secuencia de comandos puede funcionar desde la línea de comandos, pero no en el contexto donde finalmente tiene la intención de usarla.
kasperd
Gracias, esto es algo muy educativo y exactamente el tipo de cosas que esperaba aprender al comenzar este desafío. Para los casos en los que esto (o podría) importar, la #!línea en mi respuesta editada (1 línea de infraestructura con 21 caracteres) se puede combinar con la respuesta de cualquier otra persona a un costo de solo 2 líneas de Windows (de las cuales una está en blanco) o 23 caracteres
jez
2

Resumen / síntesis de respuestas y discusión

Esto fue divertido y aprendí mucho.

Algunos cruft específicos de Windows son inevitables si, en su sistema POSIX, necesita comenzar su script con una #!línea. Si no tiene otra alternativa que hacer esto, entonces esta línea:

#!/bin/sh # 2>NUL

es probablemente lo mejor que puede obtener. Hace que se genere una línea en blanco y una línea cruft en la consola de Windows. Sin embargo, es posible que pueda escapar sin un#! línea: en la mayoría de los sistemas, uno de los intérpretes de shell habituales terminará ejecutando el script (el problema es que no es universalmente predecible qué intérprete será; depende de, pero lo hará) no necesariamente ser idéntico al shell que usa para invocar el comando).

Más allá de esa complicada primera línea, había algunas soluciones sin ingenio realmente ingeniosas. La presentación ganadora de jimmy23013 consistió en solo dos líneas cortas de infraestructura, e hizo uso del doble papel del :personaje para implementar una línea "silenciosa" en ambas plataformas (como un no-op de factosh y amigos, y como una etiqueta marcador en cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Que es posible hacer tales ejecutar un script en sistemas POSIX, incluso a pesar CRLF el fin de línea, pero para hacer esto para la mayoría de los intérpretes que tienen que terminar cada línea de su sección de POSIX (incluso líneas en blanco) con un carácter de comentario o comentario.

Finalmente, aquí hay dos variantes de una solución que he desarrollado en base a los aportes de todos. Pueden ser casi el mejor de todos los mundos, ya que minimizan el daño por la falta #!y hacen que la compatibilidad con CRLF sea aún más fluida. Se necesitan dos líneas de infraestructura adicionales. El shell POSIX impredecible solo debe interpretar una línea (estandarizada), y esa línea le permite seleccionar el shell para el resto del script ( bashen el siguiente ejemplo):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
bash "$@"<<:Z
#
echo "Hello POSIX!" #
ps # let's throw this into the payload to keep track of which shell is being used
#
:Z

Parte de la belleza de estas soluciones heredoc es que siguen siendo CRLF robustas: siempre que <<:Zllegue al final de la línea, el procesador heredoc realmente buscará y encontrará el token:Z\r

Como giro final, puede deshacerse de esos molestos comentarios de fin de línea y aún conservar la robustez de CRLF, quitando los \rcaracteres antes de pasar las líneas al shell. Esto pone un poco más de fe en el shell impredecible (sería bueno usarlo en { tr -d \\r|bash;}lugar de, (tr -d \\r|bash)pero los corchetes son una sintaxis de bash-only):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
(tr -d \\r|bash "$@")<<:Z

echo "Hello POSIX!"
ps

:Z

Por supuesto, este enfoque sacrifica la capacidad de canalizar la entrada stdin en el script.

jez
fuente