Un fichier script exécutable qui s'exécute sur POSIX et Windows

16

Défi : écrire un seul fichier de script foo.cmdqui peut être invoqué à partir de l' cmd.exeinvite Windows vanille (pas PowerShell, pas en mode administrateur), pour exécuter du code arbitraire spécifique à Windows ...

> .\foo.cmd
Hello Windows! 

... mais aussi être invoquée sans modification à partir d'une invite de shell typique conforme à POSIX (Linux / OSX) (bash , tcshou zsh), pour exécuter arbitraire code spécifique POSIX:

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

... sans nécessiter l'installation ou la création d'interprètes / outils tiers.

Je sais que c'est possible, mais avec cruft (c'est-à-dire sous Windows, une ou deux lignes de déchets / message d'erreur sont imprimées sur stderr ou stdout avant "Bonjour Windows!").

Le critère gagnant est la minimisation du (premier) nombre de lignes de découpe et (deuxièmement) du nombre de caractères de gravure.

Cruft peut être défini comme n'importe quelle sortie de console (stdout ou stderr) qui n'est pas produite par le code de charge utile (arbitraire). Les lignes vides sont comptées dans le nombre de lignes. Les sauts de ligne ne sont pas comptés dans le nombre de caractères. Les scores de Cruft doivent être additionnés sur les deux plates-formes. clsMéconnaissons des mécanismes comme celui-ci qui balaient la cruauté mais au prix de masquer également la sortie du terminal précédent. Si Windows fait écho à vos commandes parce que vous ne l'avez pas @echo offencore fait, excluons les caractères qu'il dépense lors de l'impression du répertoire et de l'invite actuels.

Un critère secondaire est la simplicité / élégance de la solution à l'intérieur foo.cmd: si "infrastructure" est définie comme n'importe quel caractère non directement impliqué dans le code de charge utile arbitraire, minimisez d'abord le nombre de lignes qui contiennent des caractères d'infrastructure, puis le nombre total d'infrastructure personnages.

Félicitations supplémentaires si la partie POSIX fonctionne malgré le fichier ayant des fins de ligne CRLF! (Je ne suis pas sûr que la dernière partie soit même possible.)

Ma solution existante, que je posterai ici une fois que d'autres auront eu la chance, utilise 6 lignes de code d'infrastructure (52 caractères hors newlines). Il produit 5 lignes de découpe, dont deux sont vides, qui se produisent toutes sur Windows (30 caractères excluant les sauts de ligne et excluant le répertoire / la chaîne d'invite actuelle qui apparaît sur deux de ces lignes).

jez
la source
doit être un script shell / batch, oui?
cat
1
Quelqu'un connaît-il un environnement d'essai en ligne pour DOS?
Digital Trauma
1
J'ai trouvé celui-ci mais il ne me laisse pas entrer les caractères ":", "\", "{" ou "}": - /
Digital Trauma
@DigitalTrauma Il y a Wine (que vous auriez dû installer, si vous ne le faites pas, car c'est pratique)
cat
1
@cat thanks - Ma réponse semble fonctionner sous wine maintenant.
Digital Trauma

Réponses:

15

0 lignes de cruft, 0 caractères de cruft, 2 infra. lignes, 21 infra. caractères, CRLF ok

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

Suppression de l'autre solution.

17 caractères utilisant exit /bde la réponse de Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #
jimmy23013
la source
Candidat fort! En particulier la deuxième version (sur le critère de simplicité / élégance, c'est bien plus agréable de ne pas avoir à munir les lignes de charge utile comme le fait la première version). Cela fonctionne pour moi sur Windows et OSX. Au début, j'ai eu une ligne de cruches disant : command not foundchaque fois qu'une ligne vierge se glissait dans la charge utile posix, mais j'ai finalement compris que ce n'était pas :sur la première ligne mais plutôt à cause des CRLF non protégés par #. C'était une nouvelle pour moi qu'une #!ligne n'est pas nécessaire - qui était responsable de deux des lignes de Windows croutées dans ma version précédente.
2015
La première version est difficile à évaluer - car elle ajoute vraisemblablement les caractères : ` >&3` \ à chaque ligne de la charge utile, je suppose que vous pourriez dire que son coût d'infrastructure est arbitrairement élevé.
2015
@jez Vous calculez un score numérique pour les réponses? Si c'est le cas, vous devez clarifier la question de savoir comment procéder.
Digital Trauma
Je suis nouveau à codegolf, si TBH Je pensais que je pensais de marquer des réponses objectivement en quelque sorte. Néanmoins, je pense que la question est claire: premièrement, nombre de lignes de coupe; rompre les liens avec le nombre de personnages cruels; puis sur nombre de lignes d'infrastructure, puis nombre de caractères d'infrastructure. Je ne m'attendais pas à des solutions qui envahissent les lignes de charge utile, comme la première solution de Jimmy. Je changerai légèrement la question pour clarifier ce qui n'est pas souhaitable (désolé pour ce léger changement de poteau de but mais je ne pense pas que cela affecte sa deuxième solution ou une version de la vôtre jusqu'à présent)
jez
Il semble que nos réponses convergent ;-) Vous avez cependant l'avantage de marquer avec l'utilisation de heredoc. La finale est-elle #nécessaire?
Digital Trauma
4

Score 0 cruft + 4 lignes infra + 32 caractères infra. LF & CRLF OK.

Ceci est basé sur ce que j'ai trouvé sur ce blog , avec les bits Amiga et autres lignes inutiles supprimés. J'ai caché les lignes DOS entre guillemets commentés au lieu d'utiliser la \ligne continue, afin que cela puisse fonctionner avec CRLF et LF.

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

Avec les terminaisons de ligne DOS CRLF ou * nix LF, cela fonctionne sur Ubuntu, OSX et 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:~$ 

Pour créer cela exactement (avec des CRLF) sur une machine * nix (y compris OSX), collez ce qui suit dans un terminal:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF
Traumatisme numérique
la source
À l'air cool! dosixest aussi un joli nom. Mais sur mon Mac (OS 10.9.4, Darwin Kernel Version 13.3.0, GNU bash version 3.2.51), il ne fonctionne pas avec: ./dosix.cmd: line 13: syntax error: unexpected end of file Une idée pourquoi?
Jez
@jez assurez-vous de sauvegarder le fichier encodé en UTF-8 et de conserver les fins de ligne Windows!
chat
Ah, cela arrivait parce que j'avais sans le savoir des fins de ligne Windows et que j'utilisais la première version.
2015 à 20h42
Notez que vous pouvez entrer le caractère CR dans Bash par insertion (ou Cv), entrez (ou Cm).
jimmy23013
@jez Donc, cela marque 0 lignes de cruft + 4 lignes d'infrastructure + 32 caractères d'infrastructure. Ces chiffres devraient-ils être combinés de manière significative pour créer un seul score à des fins de comparaison?
Digital Trauma
2

Je publierai déjà la solution que j'utilisais, car elle a déjà été battue. C'est la courtoisie d'un de mes collègues qui, je pense, doit avoir lu la même entrée de blog que Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Croupe Windows: 5 lignes (dont deux vides) / 30 caractères
  • OSX cruft: 0
  • Infrastucture: 6 lignes / 52 caractères
  • Compatibilité CRLF: seulement si l'interprète nommé sur la #!ligne s'en fiche (donc pour les interprètes standard comme shet amis, il échoue)
jez
la source
Sur POSIX, un script commence toujours par #!, ce qui signifie que la vôtre est jusqu'à présent la seule réponse à être un script valide sur un système POSIX. Bien sûr, certains des autres peuvent s'exécuter si - mais seulement s'ils sont démarrés à partir d'un shell avec une solution de contournement pour les scripts défectueux.
kasperd
Bon à savoir! Je suppose que je suis flou sur les critères stricts de conformité POSIX. Mon véritable objectif est d'avoir des fichiers de script qui fonctionnent "prêts à l'emploi" sur "la plupart" des systèmes de bureau modernes, peu importe ce que cela signifie - Windows 7/8/10, Ubuntu, OSX ... Quelle est la solution universelle pour les scripts sans shebangless, Je me demande? Quoi qu'il en soit, je ne vais pas
m'attribuer
Je n'avais pas remarqué que la réponse et la question étaient écrites par la même personne. Je pense que tous les shells avec lesquels j'ai travaillé ont une solution pour une #!ligne manquante , mais ils utiliseront des shells différents pour interpréter le script. Cela signifie qu'un "script" qui ne commence pas par #!doit être valide non seulement dans un shell, mais dans tous les shell par lesquels il pourrait être interprété de manière plausible. Mais pire encore, cela ne fonctionne que lorsqu'il est démarré à partir d'un autre shell. Un tel script peut fonctionner à partir de la ligne de commande, mais pas dans le contexte où vous avez finalement l'intention de l'utiliser.
kasperd
Merci, c'est un truc très éducatif et exactement le genre de chose que j'espérais apprendre en lançant ce défi. Pour les cas où cela sera (ou pourrait) être important, la #!ligne dans ma réponse modifiée (1 ligne d'infrastructure avec 21 caractères) peut être combinée avec la réponse de quelqu'un d'autre à un coût de Windows de 2 lignes (dont l'une est vide) ou 23 caractères.
2015
2

Résumé / synthèse des réponses et discussion

C'était amusant et j'ai beaucoup appris.

Une partie spécifique à Windows est inévitable si, sur votre système POSIX, vous devez démarrer votre script avec une #!ligne. Si vous n'avez pas d'autre choix que de le faire, alors cette ligne:

#!/bin/sh # 2>NUL

est probablement le meilleur qu'il puisse obtenir. Il provoque une ligne vide et une ligne de cruft en sortie sur la console Windows. Cependant, vous pourrez peut- être vous en sortir sans #!ligne: sur la plupart des systèmes, l'un des interpréteurs de shell habituels finira par exécuter le script (le problème est qu'il n'est pas universellement prévisible de quel interprète ce sera - cela dépend, mais pas nécessairement identique au shell que vous utilisez pour appeler la commande).

Au-delà de cette première ligne délicate, il y avait des solutions sans cruauté vraiment ingénieuses. La soumission gagnante de jimmy23013 ne comprenait que deux courtes lignes d'infrastructure et utilisait le double rôle du :personnage pour mettre en œuvre une ligne "silencieuse" sur les deux plates-formes (en tant que no-op de factosh et amis, et en tant que label marqueur cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Il est possible d'exécuter un tel script sur des systèmes POSIX même malgré les fins de ligne CRLF, mais pour ce faire, pour la plupart des interprètes, vous devez terminer chaque ligne de votre section POSIX (même les lignes vides) par un commentaire ou un caractère de commentaire.

Enfin, voici deux variantes d'une solution que j'ai développée en fonction de l'apport de chacun. Ils pourraient être presque le meilleur de tous les mondes, en ce sens qu'ils minimisent les dommages causés par le manque #!et rendent la compatibilité CRLF encore plus fluide. Deux lignes d'infrastructure supplémentaires sont nécessaires. Une seule ligne (standardisée) doit être interprétée par le shell POSIX imprévisible, et cette ligne vous permet de sélectionner le shell pour le reste du script ( bashdans l'exemple suivant):

:<<@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

Une partie de la beauté de ces solutions heredoc est qu'elles sont toujours robustes au CRLF: tant que <<:Zvient à la fin de la ligne, le processeur heredoc recherchera et trouvera le jeton:Z\r

Pour finir, vous pouvez vous débarrasser de ces commentaires embêtants de fin de ligne tout en conservant la robustesse CRLF, en supprimant les \rcaractères avant de passer les lignes au shell. Cela place un peu plus de confiance dans le shell imprévisible (ce serait bien d'utiliser à la { tr -d \\r|bash;}place de (tr -d \\r|bash)mais les accolades sont une syntaxe bash uniquement):

:<<@GOTO:Z

@echo Hello Windows!

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

echo "Hello POSIX!"
ps

:Z

Bien sûr, cette approche sacrifie la possibilité de canaliser l'entrée stdin dans le script.

jez
la source