Eine ausführbare Skriptdatei, die unter POSIX und Windows ausgeführt wird

16

Herausforderung : Schreiben Sie eine einzelne Skriptdatei, foo.cmddie über die Vanilla Windows- cmd.exeEingabeaufforderung aufgerufen werden kann (nicht PowerShell, nicht im Administratormodus), um beliebigen Windows-spezifischen Code auszuführen ...

> .\foo.cmd
Hello Windows! 

... aber auch aus einem typischen POSIX-konform (Linux / OSX) Shell - Eingabeaufforderung (unverändert aufgerufen werden bash, tcshoder zsh), willkürlichen POSIX-spezifischen Code auszuführen:

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

... ohne Installation oder Erstellung von Dolmetschern / Tools von Drittanbietern.

Ich weiß, dass dies möglich ist, aber mit cruft (dh unter Windows werden ein oder zwei Zeilen Müll / Fehlermeldung vor "Hallo Windows!" Nach stderr oder stdout gedruckt).

Das Gewinnkriterium ist die Minimierung von (erstens) der Anzahl der Cruft-Linien und (zweitens) der Anzahl der Cruft-Zeichen.

Cruft kann als eine beliebige Konsolenausgabe (stdout oder stderr) definiert werden, die nicht vom (beliebigen) Nutzlastcode erzeugt wird. Leerzeilen werden in der Zeilenanzahl gezählt. Zeilenumbrüche werden bei der Zeichenanzahl nicht berücksichtigt. Cruft-Scores sollten auf beiden Plattformen summiert werden. Lassen Sie uns clssolche Mechanismen außer Acht , die die Cruft wegfegen, aber auf Kosten der Ausblendung der vorherigen Terminal-Ausgabe. Wenn Windows Ihre Befehle wiederholt, weil Sie noch nicht gedreht @echo offhaben, schließen wir die Zeichen aus, die beim Drucken des aktuellen Verzeichnisses und der Eingabeaufforderung verwendet werden.

Ein sekundäres Kriterium ist die Einfachheit / Eleganz der darin enthaltenen Lösung foo.cmd: Wenn "Infrastruktur" als ein Zeichen definiert ist, das nicht direkt in den beliebigen Nutzlastcode einbezogen ist, minimieren Sie zuerst die Anzahl der Zeilen, die Infrastrukturzeichen enthalten, und zweitens die Gesamtzahl der Infrastrukturen Zeichen.

Ein großes Lob, wenn der POSIX-Teil trotz der Datei mit CRLF-Zeilenenden funktioniert! (Bin mir nicht sicher, ob der letzte Teil überhaupt möglich ist.)

Meine vorhandene Lösung, die ich hier veröffentlichen werde, sobald andere eine Chance hatten, verwendet 6 Zeilen Infrastrukturcode (52 Zeichen ohne Zeilenumbrüche). Es werden 5 Cruft-Zeilen erzeugt, von denen zwei leer sind und alle unter Windows vorkommen (30 Zeichen ohne Zeilenumbrüche und ohne die aktuelle Verzeichnis- / Eingabeaufforderungszeichenfolge, die in zwei dieser Zeilen angezeigt wird).

jez
quelle
muss ein shell / batch script sein, oder?
Katze
1
Kennt jemand eine Try-it-Online-Umgebung für DOS?
Digital Trauma
1
Ich habe dieses gefunden , kann aber nicht die Zeichen ":", "\", "{" oder "}" eingeben: - /
Digitales Trauma
@DigitalTrauma Es gibt Wein (den Sie installiert haben sollten, wenn Sie nicht, weil es praktisch ist)
Katze
1
@cat danke - Meine Antwort scheint jetzt unter Wein zu funktionieren.
Digitales Trauma

Antworten:

15

0 cruft lines, 0 cruft chars, 2 infra. Linien, 21 infra. Zeichen, CRLF ok

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

Die andere Lösung wurde entfernt.

17 Zeichen exit /baus der Antwort von Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #
jimmy23013
quelle
Starker Anwärter! Insbesondere die zweite Version (in Bezug auf das Kriterium Einfachheit / Eleganz ist es viel schöner, die Nutzlastlinien nicht so mischen zu müssen wie die erste Version). Dies läuft für mich unter Windows und OSX. Ursprünglich bekam ich eine Zeile Cruft-Spruch, : command not foundwenn sich eine leere Zeile in die Nutzlast von posix eingeschlichen hatte, aber schließlich stellte ich fest, dass dies nicht :in der ersten Zeile, sondern aufgrund von ungeschützten CRLFs der Fall war #. Es war eine Neuigkeit für mich, dass eine #!Leitung nicht benötigt wird - das war für zwei der Leitungen von Windows Cruft in meiner vorherigen Version verantwortlich.
jez
Die erste Version ist schwer zu bewerten - da sie vermutlich die Zeichen : ` >&3` \ zu jeder Zeile der Nutzlast hinzufügt , kann man davon ausgehen, dass die Infrastrukturkosten beliebig hoch sind.
jez
@jez Du berechnest eine numerische Punktzahl für Antworten? Wenn ja, müssen Sie es in der Frage, wie das geht, viel klarer machen.
Digitales Trauma
Ich bin neu in codegolf, so TBH ich dachte , dass ich erwartete Antworten irgendwie objektiv zu bewerten. Dennoch denke ich, dass die Frage klar macht: Erstens die Anzahl der Cruft-Linien; Brechen Sie die Bindungen dazu durch die Anzahl der Cruft-Charaktere. dann nach Anzahl der Infrastrukturzeilen, dann nach Anzahl der Infrastrukturzeichen. Ich hatte keine Lösungen erwartet, die die Payload-Linien durcheinander bringen, wie die erste Lösung von Jimmy. Ich ändere die Frage ein wenig, um klar zu stellen, dass dies unerwünscht ist (Entschuldigung für die leichte Verschiebung des Torpfostens, aber ich glaube, es hat noch keine Auswirkungen auf seine zweite Lösung oder eine Ihrer Versionen)
jez
Sieht so aus, als ob unsere Antworten sich annähern ;-) Mit der Verwendung von heredoc haben Sie jedoch den Vorteil, Punkte zu erzielen. Ist das Finale #nötig?
Digitales Trauma
4

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

Dies basiert auf dem, was ich in diesem Blog gefunden habe , wobei die Amiga-Bits und andere unnötige Zeilen entfernt wurden. Ich habe die DOS-Zeilen in kommentierten Anführungszeichen ausgeblendet, anstatt \line continue zu verwenden, damit dies sowohl mit CRLF als auch mit LF funktioniert.

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

Mit den Zeilenenden DOS CRLF oder * nix LF funktioniert es unter Ubuntu, OSX und 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:~$ 

Um dies genau (mit CRLFs) auf einem * nix-Computer (einschließlich OSX) zu erstellen, fügen Sie Folgendes in ein Terminal ein:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF
Digitales Trauma
quelle
Sieht gut aus! dosixist auch ein schöner Name. Aber auf meinem Mac (OS 10.9.4, Darwin Kernel Version 13.3.0, GNU bash Version 3.2.51) funktioniert es nicht mit: Keine ./dosix.cmd: line 13: syntax error: unexpected end of file Ahnung warum?
jez
@jez stellen Sie sicher, dass Sie die mit UTF-8 codierte Datei speichern und die Windows-Zeilenenden beibehalten!
Katze
Ah, es passierte, weil ich unwissentlich Windows-Zeilenenden hatte und die erste Version verwendete.
jez
Beachten Sie, dass Sie das CR-Zeichen in Bash eingeben können, indem Sie Einfügen (oder Lebenslauf), Eingeben (oder Cm).
Jimmy23013
@jez Das ergibt also 0 Cruft-Linien + 4 Infrastruktur-Linien + 32 Infrastruktur-Zeichen. Sollten diese Zahlen in irgendeiner sinnvollen Weise kombiniert werden, um eine einzige Bewertung zum Vergleich zu erhalten?
Digitales Trauma
2

Ich werde die Lösung, die ich verwendet habe, bereits posten, da sie bereits geschlagen wurde. Es ist mit freundlicher Genehmigung eines Kollegen von mir, von dem ich denke, dass er denselben Blogeintrag wie Digital Trauma gelesen hat .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Windows cruft: 5 Zeilen (von denen zwei leer sind) / 30 Zeichen
  • OSX-Cruft: 0
  • Infrastruktur: 6 Zeilen / 52 Zeichen
  • CRLF-Kompatibilität: Nur wenn der in der #!Zeile angegebene Dolmetscher sich nicht darum kümmert (für Standarddolmetscher wie shund Freunde schlägt dies fehl)
jez
quelle
Unter POSIX beginnt ein Skript immer mit #!, was bedeutet, dass Sie die einzige Antwort sind, die auf einem POSIX-System gültig ist. Sicher, einige der anderen können ausgeführt werden, wenn - aber nur, wenn sie von einer Shell mit einer Problemumgehung für fehlerhafte Skripte gestartet werden.
Kasperd
Gut zu wissen! Ich glaube, ich bin mir der strengen Kriterien für die POSIX-Konformität nicht sicher. Mein eigentliches Ziel ist es, Skriptdateien zu haben, die auf den meisten modernen Desktop-Systemen "out of the box" funktionieren, was auch immer das bedeutet - Windows 7/8/10, Ubuntu, OSX ... Ich wundere mich? Wie auch immer, ich werde die Punkte nicht an mich vergeben :-)
jez
Mir war nicht aufgefallen, dass sowohl Antwort als auch Frage von derselben Person geschrieben wurden. Ich denke, alle Shells, mit denen ich gearbeitet habe, haben eine Problemumgehung für eine fehlende #!Zeile, aber sie verwenden unterschiedliche Shells für die Interpretation des Skripts. Das bedeutet, dass ein "Skript", das nicht mit "" beginnt, #!nicht nur in einer Shell gültig sein muss, sondern in jeder Shell, von der es plausibel interpretiert werden könnte. Schlimmer noch, es funktioniert nur, wenn es von einer anderen Shell gestartet wird. Ein solches Skript funktioniert möglicherweise über die Befehlszeile, jedoch nicht in dem Kontext, in dem Sie es endgültig verwenden möchten.
Kasperd
Danke, das ist sehr lehrreich und genau das, was ich mir erhofft hatte, als ich mit dieser Herausforderung angefangen habe. In Fällen, in denen dies von Bedeutung ist (oder sein könnte), kann die #!Zeile in meiner bearbeiteten Antwort (1 Infrastrukturzeile mit 21 Zeichen) mit der Antwort einer anderen Person zu einem Windows-Cruft-Preis von 2 Zeilen (von denen eine leer ist) oder kombiniert werden 23 Zeichen.
jez
2

Zusammenfassung / Synthese der Antworten und Diskussion

Das hat Spaß gemacht und ich habe viel gelernt.

Einige Windows-spezifische Probleme sind unvermeidlich, wenn Sie auf Ihrem POSIX-System Ihr Skript mit einer #!Zeile beginnen müssen. Wenn Sie keine andere Wahl haben, als dies zu tun, dann ist diese Zeile:

#!/bin/sh # 2>NUL

ist wahrscheinlich das Beste, was es bekommen kann. Auf der Windows-Konsole werden eine Leerzeile und eine Cruft-Zeile ausgegeben. Aber Sie können der Lage sein , ohne wegzukommen #!Linie: auf den meisten Systemen, eine der üblichen Shell - Interpreter wird die Ausführung des Skripts am Ende (das Problem ist , dass es nicht allgemein vorhersehbar ist , welche Interpreter das sein wird - es hängt davon ab, wird aber nicht unbedingt identisch sein mit der Shell, mit der Sie den Befehl aufrufen).

Jenseits dieser kniffligen ersten Zeile gab es einige wirklich geniale Lösungen, die nichts mit Krüppeln zu tun hatten. Der Gewinnerbeitrag von jimmy23013 bestand nur aus zwei kurzen Infrastrukturlinien und nutzte die doppelte Rolle des :Charakters, um auf beiden Plattformen eine "stille" Linie zu implementieren (als De-facto- No-Op-In shund Freunde sowie als Label) Markierung in cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Es ist möglich, ein solches Skript trotz CRLF-Zeilenenden auf POSIX-Systemen auszuführen. Für die meisten Interpreter müssen Sie jedoch jede Zeile Ihres POSIX-Abschnitts (auch leere Zeilen) mit einem Kommentar oder Kommentarzeichen beenden .

Schließlich sind hier zwei Varianten einer Lösung, die ich auf der Grundlage der Beiträge aller entwickelt habe. Sie könnten fast das Beste aus beiden Welten, indem sie den Schaden aus dem Mangel an minimieren #!und CRLF-Kompatibilität machen noch glatter. Es werden zwei zusätzliche Infrastrukturleitungen benötigt. Nur eine (standardisierte) Zeile muss von der unvorhersehbaren POSIX-Shell interpretiert werden. In dieser Zeile können Sie die Shell für den Rest des Skripts auswählen ( bashim folgenden Beispiel):

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

Das Schöne an diesen heredoc-Lösungen ist, dass sie immer noch CRLF-robust sind: Solange <<:Zdas Ende der Reihe erreicht ist, sucht und findet der heredoc-Prozessor das Token:Z\r

Als letzte Wendung können Sie diese lästigen Zeilenende-Kommentare loswerden und trotzdem die CRLF-Robustheit beibehalten, indem Sie die \rZeichen entfernen, bevor Sie die Zeilen an die Shell übergeben. Dies stellt etwas mehr Vertrauen in den unberechenbaren Schale (es wäre schön, zu verwenden , { tr -d \\r|bash;}anstatt (tr -d \\r|bash)aber geschweiften Klammern sind bash-only - Syntax):

:<<@GOTO:Z

@echo Hello Windows!

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

echo "Hello POSIX!"
ps

:Z

Dieser Ansatz beeinträchtigt natürlich die Möglichkeit, Standardeingaben in das Skript zu leiten.

jez
quelle