Plik wykonywalny skryptu, który działa na POSIX i Windows

16

Wyzwanie : napisz pojedynczy plik skryptu, foo.cmdktóry można wywołać z waniliowego cmd.exemonitu Windows (nie PowerShell, nie w trybie administratora), aby wykonać dowolny kod specyficzny dla Windows ...

> .\foo.cmd
Hello Windows! 

... ale również powoływać niezmienione od typowego POSIX (Linux / OSX) shell prompt ( bash, tcshlub zsh), aby wykonać dowolny POSIX-specyficzny kod:

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

... bez konieczności instalowania lub tworzenia zewnętrznych tłumaczy / narzędzi.

Wiem, że jest to możliwe, ale w przypadku cruft (tj. W systemie Windows jedna lub dwie linie śmieci / komunikat o błędzie są drukowane na stderr lub stdout przed „Hello Windows!”).

Kryterium wygranej jest minimalizacja (pierwszej) liczby linii cruft i (drugiej) liczby znaków cruft.

Cruft można zdefiniować jako dowolne wyjście konsoli (standardowe lub standardowe), które nie jest generowane przez (dowolny) kod ładunku. Puste linie są liczone w liczbie wierszy. Nowe linie nie są liczone w liczbie znaków. Wyniki Cruft należy zsumować na obu platformach. Nie bierzmy pod uwagę takich mechanizmów, clsktóre wymiatają cruft, ale kosztem wyczyszczenia również poprzedniego wyjścia terminala. Jeśli system Windows powtórzy polecenia, ponieważ jeszcze tego nie zrobiłeś @echo off, wykluczmy znaki, które spędza na drukowaniu bieżącego katalogu i monit.

Drugim kryterium jest prostota / elegancja zastosowanego rozwiązania foo.cmd: jeśli „infrastruktura” jest zdefiniowana jako dowolny znak nieobjęty bezpośrednio dowolnym kodem ładunku, to najpierw należy zminimalizować liczbę wierszy zawierających znaki infrastruktury, a następnie całkowitą liczbę infrastruktury postacie.

Dodatkowe wyróżnienia, jeśli część POSIX będzie działać pomimo tego, że plik ma zakończenia linii CRLF! (Nie jestem pewien, czy ostatnia część jest nawet możliwa.)

Moje istniejące rozwiązanie, które opublikuję tutaj, gdy inni będą mieli okazję, wykorzystuje 6 linii kodu infrastruktury (52 znaki z wyłączeniem nowych linii). Tworzy 5 wierszy cruft, z których dwa są puste, z których wszystkie występują w systemie Windows (30 znaków z wyłączeniem nowych wierszy i wykluczenia bieżącego ciągu katalogu / zachęty, który pojawia się w dwóch z tych wierszy).

jez
źródło
to musi być skrypt powłoki / partii, tak?
kot
1
Czy ktoś wie o środowisku try-it-online dla DOS?
Cyfrowy uraz
1
Znalazłem ten, ale nie pozwala mi wpisać znaków „:”, „\”, „{” lub „}”: - /
Digital Trauma
@DigitalTrauma Jest wino (które powinieneś zainstalować, jeśli nie, bo jest to przydatne)
kot
1
@cat dzięki - moja odpowiedź wydaje się teraz działać pod winem.
Cyfrowy uraz

Odpowiedzi:

15

0 linii cruft, 0 znaków cruft, 2 infra. linie, 21 infra. znaki, CRLF ok

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

Usunięto inne rozwiązanie.

17 znaków korzystających exit /bz odpowiedzi Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #
jimmy23013
źródło
Silny pretendent! W szczególności druga wersja (według kryterium prostoty / elegancji o wiele fajniej jest nie musieć modyfikować linii ładunku tak, jak robi to pierwsza wersja). Działa to dla mnie w systemie Windows i OSX. Początkowo dostałem jedną linię cruft, mówiącą, że : command not foundgdy pusta linia wkradła się do ładunku posix, ale w końcu zorientowałem się, że nie chodzi :o pierwszą linię, ale raczej z powodu braku ochrony CRLF #. To była dla mnie wiadomość, że #!linia nie jest potrzebna - która była odpowiedzialna za dwie linie Windows Cruft w mojej poprzedniej wersji.
jez
Pierwsza wersja jest trudna do oceny - ponieważ prawdopodobnie dodaje znaki : ` >&3` \ do każdej linii ładunku, myślę, że można powiedzieć, że jej koszt infrastruktury jest arbitralnie wysoki.
jez
@jez Obliczasz wynik liczbowy dla odpowiedzi? Jeśli tak, musisz wyjaśnić pytanie, jak to zrobić.
Cyfrowy uraz
Jestem nowy na codegolf, więc TBH Myślałam, że oczekuje , aby zdobyć odpowiedź obiektywnie jakoś. Niemniej jednak myślę, że pytanie wyjaśnia: po pierwsze, liczba linii cruft; zerwać z tym więzi przez liczbę znaków cruft; następnie na liczbie linii infrastruktury, a następnie na liczbie znaków infrastruktury. Nie spodziewałem się rozwiązań, które ocalą linie ładunkowe, jak pierwsze rozwiązanie Jimmy'ego. Lekko zmienię pytanie, aby wyjaśnić, że jest to niepożądane (przepraszam za tę drobną zmianę bramki, ale nie sądzę, żeby miało to wpływ na jego drugie rozwiązanie lub jak dotąd twoją wersję)
jez
Wygląda na to, że nasze odpowiedzi są zbieżne ;-) Jednak masz przewagę w punktowaniu dzięki użyciu heredoc. Czy końcowy jest #konieczny?
Cyfrowy uraz
4

Zdobądź 0 cruft + 4 infra linie + 32 infra znaki. LF i CRLF OK.

Jest to oparte na tym, co znalazłem na tym blogu , z usuniętymi bitami Amigi i innymi niepotrzebnymi liniami. Ukryłem wiersze DOS w komentarzach zamiast używać \kontynuacji wiersza, aby działało to zarówno z CRLF, jak i LF.

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

Z końcówkami linii DOS CRLF lub * nix LF, działa na Ubuntu, OSX i 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:~$ 

Aby utworzyć to dokładnie (z CRLF) na maszynie * nix (w tym OSX), wklej następujące elementy do terminala:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF
Cyfrowa trauma
źródło
Wygląda dobrze! dosixto też miłe imię. Ale na moim Macu (OS 10.9.4, jądro Darwina w wersji 13.3.0, GNU bash w wersji 3.2.51) nie działa z: ./dosix.cmd: line 13: syntax error: unexpected end of file Wiesz, dlaczego?
jez
@jez pamiętaj, aby zapisać plik zakodowany za pomocą UTF-8 i zachować zakończenia linii Windows!
kot
Ach, działo się tak, ponieważ nieświadomie miałem zakończenia linii Windows i korzystałem z pierwszej wersji.
jez
Zauważ, że możesz wprowadzić znak CR w Bash, wstawiając (lub Cv), wprowadzając (lub Cm).
jimmy23013
@jez Wynik ten wynosi 0 linii cruft + 4 linie infrastruktury + 32 znaki infrastruktury. Czy te liczby należy łączyć w jakikolwiek sensowny sposób, aby stworzyć pojedynczy wynik do porównania?
Cyfrowy uraz
2

Już opublikuję rozwiązanie, którego używałem, ponieważ zostało już pobite. Dzięki uprzejmości mojego kolegi, który moim zdaniem musiał przeczytać ten sam wpis na blogu, co Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Windows cruft: 5 linii (z których dwie są puste) / 30 znaków
  • Cruft OSX: 0
  • Struktura: 6 linii / 52 znaki
  • Kompatybilność z CRLF: tylko jeśli tłumacz #!nie jest zainteresowany (więc w przypadku standardowych tłumaczy, takich jak shi przyjaciele, nie działa)
jez
źródło
W systemie POSIX skrypt zawsze zaczyna się od #!, co oznacza, że ​​twoja jedyna jak dotąd odpowiedź jest poprawnym skryptem w systemie POSIX. Pewnie niektóre z pozostałych mogą działać, jeśli - ale tylko wtedy, gdy są uruchamiane z powłoki z obejściem wadliwych skryptów.
kasperd
Dobrze wiedzieć! Wydaje mi się, że nie rozumiem ścisłych kryteriów zgodności z POSIX. Moim prawdziwym celem jest posiadanie plików skryptów, które działają „od razu po wyjęciu z pudełka” na „większości” nowoczesnych systemów komputerowych, cokolwiek to znaczy - Windows 7/8/10, Ubuntu, OSX ... Jak uniwersalne jest obejście dla skryptów bez shebang, Zastanawiam się? W każdym razie nie zamierzam przyznawać sobie punktów :-)
jez
Nie zauważyłem, że zarówno odpowiedź, jak i pytanie zostały napisane przez tę samą osobę. Myślę, że wszystkie powłoki, z którymi pracowałem, mają obejście dla brakującej #!linii, ale będą używać różnych powłok do interpretacji skryptu. Oznacza to, że „skrypt”, który się nie zaczyna, #!musi być poprawny nie tylko w jednej powłoce, ale w każdej powłoce, którą mógłby być interpretowany. Co gorsza, działa tylko wtedy, gdy jest uruchamiany z innej powłoki. Taki skrypt może działać z poziomu wiersza poleceń, ale nie w kontekście, w którym ostatecznie zamierzasz go używać.
kasperd
Dziękuję, to bardzo pouczające rzeczy i właśnie tego chciałem się nauczyć, rozpoczynając to wyzwanie. W przypadkach, w których będzie to (lub mogłoby) mieć znaczenie, #!wiersz w mojej edytowanej odpowiedzi (1 linia infrastruktury z 21 znakami) może być połączony z odpowiedzią innej osoby przy koszcie tylko dwóch wierszy (z których jeden jest pusty) lub 23 znaki.
jez
2

Podsumowanie / synteza odpowiedzi i dyskusja

To była świetna zabawa i wiele się nauczyłem.

Niektóre crufty specyficzne dla systemu Windows są nieuniknione, jeśli w systemie POSIX konieczne jest uruchomienie skryptu za pomocą #!wiersza. Jeśli nie masz innego wyjścia, jak to zrobić, to ten wiersz:

#!/bin/sh # 2>NUL

jest prawdopodobnie najlepszym, co może dostać. Powoduje to, że jedna konsola Windows wyświetla jeden pusty wiersz i jeden wiersz cruft. Jednakże, może być w stanie uciec bez #!linii: na większości systemów, jeden z typowych tłumaczy powłoki zakończy się wykonywanie skryptu (problem jest to, że nie jest powszechnie przewidywalne który interpreter, że będzie - to zależy, ale będzie niekoniecznie musi być identyczny z powłoką używaną do wywołania polecenia).

Poza tą trudną pierwszą linią pojawiły się naprawdę genialne rozwiązania bezkrwiste. Zwycięskie zgłoszenie autorstwa jimmy23013 składało się tylko z dwóch krótkich linii infrastruktury i wykorzystało podwójną rolę :postaci do wprowadzenia „cichej” linii na obu platformach (jako de facto nieobecność shi znajomi oraz jako etykieta znacznik w cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Możliwe jest uruchomienie takiego skryptu w systemach POSIX, nawet pomimo zakończenia linii CRLF, ale aby to zrobić dla większości interpreterów, musisz zakończyć każdy wiersz sekcji POSIX (nawet puste wiersze) znakiem komentarza lub komentarza.

Wreszcie, oto dwa warianty rozwiązania, które opracowałem w oparciu o wkład wszystkich. Mogą być prawie najlepsze ze wszystkich światów, ponieważ minimalizują szkody wynikające z braku #!i sprawiają, że kompatybilność z CRLF jest jeszcze bardziej płynna. Potrzebne są dwie dodatkowe linie infrastruktury. Tylko jedna (znormalizowana) linia musi być interpretowana przez nieprzewidywalną powłokę POSIX, a ta linia pozwala wybrać powłokę dla reszty skryptu ( bashw poniższym przykładzie):

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

Zaletą tych rozwiązań heredoc jest to, że wciąż są one odporne na CRLF: tak długo, jak będzie <<:Zna końcu linii, procesor heredoc będzie faktycznie szukał i znajdzie token:Z\r

Na koniec możesz pozbyć się tych irytujących komentarzy na końcu linii i nadal zachować odporność na CRLF, usuwając \rznaki przed przekazaniem linii do powłoki. Powoduje to nieco większą wiarę w nieprzewidywalną powłokę (byłoby miło użyć { tr -d \\r|bash;}zamiast tego, (tr -d \\r|bash)ale nawiasy klamrowe to tylko składnia bash):

:<<@GOTO:Z

@echo Hello Windows!

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

echo "Hello POSIX!"
ps

:Z

Oczywiście takie podejście poświęca zdolność do przesyłania danych wejściowych standardowego wejścia do skryptu.

jez
źródło