Un file di script eseguibile che viene eseguito su POSIX e Windows


16

Sfida : scrivere un singolo file di script foo.cmdche può essere richiamato dal cmd.exeprompt di Windows vanilla (non PowerShell, non in modalità amministratore), per eseguire codice arbitrario specifico per Windows ...

> .\foo.cmd
Hello Windows! 

... ma anche essere richiamato inalterato da una tipica POSIX (Linux / OSX) Shell Prompt ( bash, tcsho zsh), per eseguire codice arbitrario POSIX-specifico:

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

... senza richiedere l'installazione o la creazione di interpreti / strumenti di terze parti.

So che è possibile, ma con cruft (cioè su Windows, una o due righe di immondizia / messaggio di errore vengono stampate su stderr o stdout prima di "Ciao Windows!").

Il criterio vincente è la minimizzazione del (primo) numero di linee di cruft e (secondo) del numero di caratteri di cruft.

Cruft può essere definito come qualsiasi output di console (stdout o stderr) che non è prodotto dal codice di payload (arbitrario). Le righe vuote vengono conteggiate nel conteggio delle righe. Le nuove righe non vengono conteggiate nel conteggio dei personaggi. I punteggi di Cruft devono essere sommati su entrambe le piattaforme. Ignoriamo meccanismi del genere clsche spazzano via l'innesto ma a costo di oscurare anche l'output del terminale precedente. Se Windows echi i tuoi comandi perché non hai ancora girato @echo off, escludiamo i caratteri che spende nella stampa della directory corrente e del prompt.

Un criterio secondario è la semplicità / eleganza della soluzione interna foo.cmd: se "infrastruttura" è definita come qualsiasi carattere non direttamente coinvolto nel codice di payload arbitrario, quindi minimizzare prima il numero di righe che contengono caratteri di infrastruttura e in secondo luogo il numero totale di infrastrutture personaggi.

Complimenti extra se la parte POSIX funzionerà nonostante il file abbia terminazioni di riga CRLF! (Non sono sicuro che l'ultima parte sia persino possibile.)

La mia soluzione esistente, che posterò qui una volta che gli altri avranno avuto una possibilità, utilizza 6 righe di codice di infrastruttura (52 caratteri esclusi i newline). Produce 5 righe di cruft, due delle quali sono vuote, tutte presenti su Windows (30 caratteri escludendo le nuove righe ed escludendo l'attuale stringa directory / prompt che appare su due di quelle righe).


deve essere uno script shell / batch, vero?
cat

1
Qualcuno sa di un ambiente di prova online per DOS?
Digital Trauma,

1
Ho trovato questo ma non mi permette di inserire i caratteri ":", "\", "{" o "}": - /
Digital Trauma

@DigitalTrauma C'è Wine (che avresti dovuto installare, in caso contrario, perché è utile)
cat

1
@cat grazie - La mia risposta sembra funzionare ora con il vino.
Digital Trauma,

Risposte:


15

0 linee di cruft, 0 caratteri di cruft, 2 infra. linee, 21 infra. caratteri, CRLF ok

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

Rimossa l'altra soluzione.

17 caratteri usando exit /bdalla risposta di Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #

Contendente forte! In particolare la seconda versione (sul criterio della semplicità / eleganza, è molto più bello non dover confondere le linee di carico come fa la prima versione). Questo funziona per me su Windows e OSX. Inizialmente ho ottenuto una riga di cruft che diceva : command not foundogni volta che una riga vuota si insinuava nel payload posix, ma alla fine ho capito che non si trattava :della prima riga ma piuttosto a causa dei CRLF non protetti da# . Era una novità per me che #!non fosse necessaria una linea, che era responsabile di due delle linee di Windows Cruft nella mia versione precedente.
jez,

La prima versione è difficile da segnare, poiché presumibilmente aggiunge i personaggi : ` >&3` \ a ogni riga del payload, immagino che si possa dire che il suo costo di infrastruttura è arbitrariamente alto.
jez,

@jez Stai calcolando un punteggio numerico per le risposte? In tal caso, è necessario chiarire maggiormente la domanda su come farlo.
Trauma digitale,

Sono nuovo di Codegolf, quindi TBH pensavo che avrei dovuto segnare le risposte in modo obiettivo in qualche modo. Tuttavia, penso che la domanda chiarisca: in primo luogo, il numero di linee di crociera; rompere i legami su questo per numero di personaggi cruft; quindi sul numero di linee dell'infrastruttura, quindi sul numero di caratteri dell'infrastruttura. Non mi aspettavo soluzioni che affondassero le linee di carico utile, come la prima soluzione di Jimmy. Cambierò leggermente la domanda per chiarire che è indesiderabile (scusate per quel leggero spostamento del goalpost ma non penso che influisca sulla sua seconda soluzione o su qualsiasi versione della vostra finora)
jez

Sembra che le nostre risposte stiano convergendo ;-) Hai il vantaggio di segnare con l'uso ereditario. Il finale è #necessario?
Digital Trauma,

4

Punteggio 0 cruft + 4 infra lines + 32 infra chars. LF & CRLF OK.

Questo si basa su quello che ho trovato in questo blog , con i bit di Amiga e altre righe inutili rimosse. Ho nascosto le righe DOS tra virgolette commentate invece di usare la \linea continua, in modo che possa funzionare sia con CRLF che con LF.

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

Con terminazioni di riga DOS CRLF o * nix LF, funziona su Ubuntu, OSX e 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:~$ 

Per crearlo esattamente (con CRLF) su una macchina * nix (incluso OSX), incollare quanto segue su un terminale:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF

Sembra bello! dosixè anche un bel nome. Ma sul mio Mac (OS 10.9.4, Darwin Kernel Versione 13.3.0, GNU bash versione 3.2.51) non funziona con: ./dosix.cmd: line 13: syntax error: unexpected end of file Qualche idea sul perché?
jez,

@jez assicurati di salvare il file codificato con UTF-8 e di preservare le terminazioni di linea di Windows!
cat

Ah, stava succedendo perché avevo inconsapevolmente terminazioni di linea di Windows e stavo usando la prima versione.
jez

Nota che puoi inserire il carattere CR in Bash con insert (o Cv), enter (o Cm).
jimmy23013,

@jez Quindi questo vale come 0 linee di trapianto + 4 linee di infrastruttura + 32 caratteri di infrastruttura. Questi numeri dovrebbero essere combinati in qualche modo significativo per creare un singolo punteggio per il confronto?
Digital Trauma,

2

Pubblicherò già la soluzione che stavo usando, poiché è già stata battuta. Per gentile concessione di un mio collega che penso debba aver letto lo stesso post di blog di Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Windows cruft: 5 righe (di cui due vuote) / 30 caratteri
  • OSX cruft: 0
  • Infrastruttura: 6 linee / 52 caratteri
  • Compatibilità CRLF: solo se l'interprete nominato sulla #!linea non si preoccupa (quindi per interpreti standard come she amici, fallisce)

Su POSIX inizia sempre uno script #!, il che significa che la tua è l'unica risposta finora ad essere uno script valido su un sistema POSIX. Sicuramente alcuni degli altri possono essere eseguiti se - ma solo se vengono avviati da una shell con una soluzione alternativa per gli script difettosi.
Kasperd,

Buono a sapersi! Immagino di essere confuso sui rigidi criteri per la conformità POSIX. Il mio vero obiettivo è avere file di script che funzionino "out of the box" sulla "maggior parte" dei sistemi desktop moderni, qualunque cosa significhi - Windows 7/8/10, Ubuntu, OSX ... Quanto è universale la soluzione alternativa per gli script shebangless, Mi chiedo? Comunque, non assegnerò i punti a me stesso :-)
jez

Non avevo notato che sia la risposta che la domanda erano state scritte dalla stessa persona. Penso che tutte le shell con cui ho lavorato abbiano una soluzione alternativa per una #!riga mancante , ma useranno shell diverse per interpretare lo script. Ciò significa che uno "script" che non inizia con #!deve essere valido non solo in una shell, ma in ogni shell che potrebbe essere plausibilmente interpretata da. Ma peggio ancora, funziona solo quando viene avviato da un'altra shell. Tale script può funzionare dalla riga di comando, ma non nel contesto in cui si intende finalmente utilizzarlo.
Kasperd,

Grazie, questa è roba molto educativa ed esattamente il genere di cose che speravo di imparare iniziando questa sfida. Per i casi in cui ciò (o potrebbe) importare, la #!riga nella mia risposta modificata (1 riga di infrastruttura con 21 caratteri) può essere combinata con la risposta di chiunque altro a un costo di crociera di solo 2 righe di Windows (di cui uno vuoto) o 23 caratteri.
jez

2

Riepilogo / sintesi di risposte e discussione

È stato divertente e ho imparato molto.

Alcune cruft specifiche per Windows sono inevitabili se, sul tuo sistema POSIX, devi avviare lo script con una #!linea. Se non hai altra alternativa che farlo, allora questa riga:

#!/bin/sh # 2>NUL

è probabilmente il meglio che può ottenere. Fa sì che una riga vuota e una riga di cruft vengano emesse sulla console di Windows. Tuttavia, si potrebbe essere in grado di uscire senza una #!riga: sulla maggior parte dei sistemi, uno dei soliti interpreti shell finirà esecuzione dello script (il problema è che non è universalmente prevedibile che interprete che sarà - dipende, ma lo farà non necessariamente identico a, la shell che usi per invocare il comando).

Al di là di quella prima linea complicata, c'erano alcune soluzioni davvero ingegnose e senza rotta. La presentazione vincente di jimmy23013 consisteva solo due righe di infrastrutture brevi, e ha fatto uso del doppio ruolo del :personaggio di realizzare una linea di "silenziosa" su entrambe le piattaforme (come di fatto no-op insh e amici, e come etichetta marker in cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Lo è possibile effettuare tale Esegui script una su sistemi POSIX anche nonostante CRLF line-finali, ma per fare questo per la maggior parte degli interpreti si deve finire ogni riga del vostro sezione POSIX (anche righe vuote) con un carattere commento o un commento.

Infine, ecco due varianti di una soluzione che ho sviluppato sulla base degli input di tutti. Potrebbero essere quasi i migliori di tutti i mondi, in quanto minimizzano i danni causati dalla mancanza #!e rendono la compatibilità CRLF ancora più fluida. Sono necessarie due linee di infrastruttura extra. Solo una riga (standardizzata) deve essere interpretata dall'imprevedibile shell POSIX, e quella riga consente di selezionare la shell per il resto dello script ( bashnell'esempio seguente):

:<<@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 della bellezza di queste soluzioni heredoc è che sono ancora robuste CRLF: finché <<:Zarriva alla fine della linea, il processore heredoc sarà effettivamente alla ricerca e troverà il token:Z\r

Come ultimo colpo, puoi sbarazzarti di quei fastidiosi commenti di fine riga e conservare comunque la robustezza del CRLF, mettendo a nudo il \r caratteri prima di passare le linee alla shell. Questo pone un po 'più di fiducia nella shell imprevedibile (sarebbe bello da usare { tr -d \\r|bash;}invece di (tr -d \\r|bash)ma le parentesi graffe sono sintassi solo bash):

:<<@GOTO:Z

@echo Hello Windows!

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

echo "Hello POSIX!"
ps

:Z

Naturalmente, questo approccio sacrifica la capacità di reindirizzare l'input di stdin nella sceneggiatura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.