Czy można napisać pojedynczy plik skryptu, który będzie wykonywany zarówno w systemie Windows (traktowany jako .bat), jak i Linux (przez Bash)?
Znam podstawową składnię obu, ale nie rozgryzłem. Prawdopodobnie może wykorzystać niejasną składnię Basha lub usterkę procesora wsadowego systemu Windows.
Polecenie do wykonania może składać się z jednej linii do wykonania innego skryptu.
Motywacją jest posiadanie tylko jednego polecenia rozruchu aplikacji dla systemu Windows i Linux.
Aktualizacja: Potrzeba "natywnego" skryptu powłoki systemu polega na tym, że musi on wybrać odpowiednią wersję interpretera, dostosować się do pewnych dobrze znanych zmiennych środowiskowych itp. Instalowanie dodatkowych środowisk, takich jak CygWin, nie jest preferowane - chciałbym zachować koncepcję " pobierz i uruchom ”.
Jedynym innym językiem do rozważenia w systemie Windows jest Windows Scripting Host - WSH, który jest domyślnie ustawiony od 98.
źródło
cp
wbudowane, lub odwrotnie, więc napisanie dwóch oddzielnych skryptów może być lepsze z pedagogiki niż pokazane tutaj zaawansowane techniki.Odpowiedzi:
To, co zrobiłem, to użycie składni etykiety cmd jako znacznika komentarza . Znak etykiety, dwukropek (
:
), jest równoważnytrue
w większości powłok POSIXish. Jeśli natychmiast po znaku etykiety pojawi się inny znak, którego nie można użyć w aGOTO
, to komentowaniecmd
skryptu nie powinno wpływać nacmd
kod.Hack polega na umieszczeniu linii kodu po sekwencji znaków „
:;
”. Jeśli piszesz głównie skrypty jednowierszowe lub, jak może być, możesz napisać jedną linięsh
dla wielu wierszycmd
, poniższe mogą być w porządku. Nie zapominaj, że każde użycie$?
musi nastąpić przed następnym dwukropkiem,:
ponieważ:
resetuje się$?
do 0.Bardzo wymyślny przykład ochrony
$?
:Innym pomysłem na pominięcie
cmd
kodu jest użycie heredoców , któresh
traktującmd
kod jako nieużywany ciąg icmd
interpretują go. W tym przypadku upewniamy się, że separator naszego heredoc jest zarówno cytowany (aby niesh
wykonywać jakiejkolwiek interpretacji jego zawartości podczas uruchamiania zsh
), jak i zaczyna się od:
tak, żecmd
przeskakiwał go jak każdy inny wiersz zaczynający się od:
.W zależności od Twoich potrzeb lub stylu kodowania przeplot
cmd
ish
kod mogą mieć sens lub nie. Jedną z metod wykonywania takiego przeplotu jest użycie heredoców. Można to jednak rozszerzyć za pomocąGOTO
techniki :Komentarze uniwersalne można oczywiście wykonać za pomocą sekwencji znaków
: #
lub:;#
. Spacja lub średnik są konieczne, ponieważsh
rozważa#
się je za część nazwy polecenia, jeśli nie jest pierwszym znakiem identyfikatora. Na przykład możesz chcieć napisać uniwersalne komentarze w pierwszych wierszach pliku przed użyciemGOTO
metody do podzielenia kodu. Następnie możesz poinformować czytelnika, dlaczego Twój skrypt jest napisany tak dziwnie:Stąd kilka pomysłów i sposobów tworzenia
sh
icmd
kompatybilnych skryptów bez poważnych skutków ubocznych, o ile wiem (i bezcmd
wyjścia'#' is not recognized as an internal or external command, operable program or batch file.
).źródło
.cmd
rozszerzenia skryptu , w przeciwnym razie nie będzie działać w systemie Windows. Czy jest jakieś obejście?.cmd
i oznaczyć jako wykonywalne. W ten sposób wykonanie skryptu z domyślnej powłoki na Windows lub unix będzie działać (testowane tylko z bash). Ponieważ nie mogę wymyślić sposobu na włączenie shebanga bez głośnego narzekania cmd, musisz zawsze wywoływać skrypt przez powłokę. To znaczy , upewnij się, że używaszexeclp()
lubexecvp()
(myślę, żesystem()
robi to „we właściwy” sposób dla Ciebie) zamiastexecl()
lubexecv()
(te ostatnie funkcje będą działać tylko w skryptach z shebangami, ponieważ trafiają bardziej bezpośrednio do systemu operacyjnego).<Exec/>
MSBuild Task ), a nie jako niezależne pliki wsadowe. Może jeśli będę miał jakiś czas w przyszłości, zaktualizuję odpowiedź o informacje w tych komentarzach…możesz spróbować tego:
Prawdopodobnie będziesz musiał użyć
/r/n
jako nowej linii zamiast stylu uniksowego.Jeśli pamiętam poprawne, nowa linia uniksowa nie jest interpretowana jako nowa linia przez skrypty Innym.bat
sposobem jest utworzenie#.exe
pliku w ścieżce, który nic nie robi podobnie jak moja odpowiedź tutaj: czy można osadzić i uruchomić VBScript w pliku wsadowym bez użycia pliku tymczasowego?EDYTOWAĆ
Odpowiedź binki jest prawie idealna, ale nadal można ją poprawić:
Używa ponownie
:
sztuczki i komentarza wieloliniowego. Wygląda na to, że cmd.exe (przynajmniej w systemie Windows10) działa bez problemów z EOL w stylu unix, więc upewnij się, że twój skrypt jest konwertowany do formatu linux. (to samo podejście zostało zastosowane tutaj i tutaj ). Chociaż użycie shebang nadal da zbędne wyjście ...źródło
/r/n
na końcu linii spowoduje wiele bólu głowy w skrypcie bash, chyba że zakończysz każdą linię znakiem#
(tj.#/r/n
) - w ten sposób\r
zostanie potraktowany jako część komentarza i zignorowany.# 2>NUL & ECHO Welcome to %COMSPEC%.
udaje mu się ukryć błąd dotyczący#
polecenia, którego nie znalezionocmd
. Ta technika jest przydatna, gdy musisz napisać samodzielną linię, która będzie wykonywana tylko przezcmd
(np. W aMakefile
).Chciałem skomentować, ale w tej chwili mogę tylko dodać odpowiedź.
Podane techniki są doskonałe i również z nich korzystam.
Trudno jest zachować plik, który zawiera dwa rodzaje podziałów wierszy, czyli
/n
część bash i/r/n
dla części okna. Większość edytorów próbuje wymusić wspólny schemat łamania linii, zgadując, jaki rodzaj pliku edytujesz. Ponadto większość metod przesyłania pliku przez Internet (szczególnie jako plik tekstowy lub skrypt) usuwa podziały wierszy, więc można zacząć od jednego rodzaju podziału wiersza, a skończyć na drugim. Jeśli przyjąłeś założenia dotyczące łamania linii, a następnie przekazałeś swój skrypt komuś innemu, może się okazać, że to nie zadziała.Innym problemem są systemy plików zamontowane w sieci (lub dyski CD), które są współdzielone między różnymi typami systemów (szczególnie tam, gdzie nie można kontrolować oprogramowania dostępnego dla użytkownika).
Dlatego należy używać podziału wiersza DOS,
/r/n
a także chronić skrypt bash przed DOS/r
, umieszczając komentarz na końcu każdej linii (#
). Nie można również używać kontynuacji linii w bash, ponieważ/r
spowoduje to ich zerwanie.W ten sposób ktokolwiek używa skryptu iw jakimkolwiek środowisku, będzie on działał.
Używam tej metody w połączeniu z tworzeniem przenośnych plików Makefile!
źródło
Poniższe działa dla mnie bez żadnych błędów lub komunikatów o błędach z Bash 4 i Windows 10, w przeciwieństwie do powyższych odpowiedzi. Nazywam plik "cokolwiek.cmd", robię,
chmod +x
żeby był wykonywalny w Linuksie i sprawiam, że ma końcówki linii unix (dos2unix
), aby bash był cichy.źródło
Możesz udostępniać zmienne:
źródło
Istnieje kilka sposobów wykonywania różnych poleceń w tym samym skrypcie
bash
i zacmd
pomocą tego samego skryptu.cmd
zignoruje wiersze zaczynające się od:;
, jak wspomniano w innych odpowiedziach. Zignoruje również następną linię, jeśli bieżąca linia kończy się poleceniemrem ^
, ponieważ^
znak uniknie końca linii, a następna linia będzie traktowana jako komentarz przezrem
.Jeśli chodzi o
bash
ignorowaniecmd
wierszy, istnieje wiele sposobów. Wyliczyłem kilka sposobów, aby to zrobić bez łamaniacmd
poleceń:Nieistniejące
#
polecenie (niezalecane)Jeśli po uruchomieniu skryptu nie jest
#
dostępne żadne poleceniecmd
, możemy to zrobić:#
Charakter na początkucmd
marek liniibash
traktują tę linię jako komentarz.#
Znaków na końcubash
linii jest używany do ustosunkowania się,\r
znak, jak Brian Tompsett wskazał w swojej odpowiedzi . Bez tegobash
zgłosi błąd, jeśli plik ma\r\n
zakończenia linii, wymagane przezcmd
.W ten
# 2>nul
sposób oszukujemy,cmd
aby zignorować błąd jakiegoś nieistniejącego#
polecenia, jednocześnie wykonując następujące polecenie.Nie używaj tego rozwiązania, jeśli
#
polecenie jest dostępne na stroniePATH
lub jeśli nie masz kontroli nad poleceniami dostępnymi dlacmd
.Używając,
echo
aby zignorować#
włączony znakcmd
Możemy użyć
echo
z przekierowanym wyjściem do wstawianiacmd
poleceń wbash
zakomentowanym obszarze:Ponieważ
#
znak nie ma specjalnego znaczeniacmd
, jest traktowany jako część tekstu doecho
. Wszystko, co musieliśmy zrobić, to przekierować wyjścieecho
polecenia i wstawić po nim inne polecenia.Pusty
#.bat
plikecho >/dev/null # 1>nul 2> #.bat
Linia tworzy pusty#.bat
plik while oncmd
(lub zastępuje istniejące#.bat
, jeśli w ogóle), a nie robi nic na whilebash
.Plik ten będzie używany przez
cmd
linię (S), który następuje, nawet jeśli jest jakaś inna#
komenda naPATH
.del #.bat
Polecenia nacmd
kodzie swoistej wobec usuwa plik, który został utworzony. Musisz to zrobić tylko w ostatniejcmd
linii.Nie używaj tego rozwiązania, jeśli
#.bat
plik może znajdować się w Twoim bieżącym katalogu roboczym, ponieważ zostanie on usunięty.Zalecane: używanie dokumentu here do ignorowania
cmd
poleceń nabash
Umieszczając
^
znak na końcucmd
linii, unikamy podziału wiersza i używając:
jako separatora dokumentu w tym miejscu, zawartość linii separatora nie będzie miała wpływu nacmd
. W ten sposóbcmd
wykona swoją linię dopiero po jej zakończeniu:
, zachowując się tak samo jakbash
.Jeśli chcesz mieć wiele linii na obu platformach i wykonywać je tylko na końcu bloku, możesz to zrobić:
Dopóki nie ma
cmd
dokładnej liniihere-document delimiter
, to rozwiązanie powinno działać. Możesz zmienićhere-document delimiter
na dowolny inny tekst.We wszystkich przedstawionych rozwiązaniach polecenia będą wykonywane dopiero po ostatniej linii , dzięki czemu ich zachowanie będzie spójne, jeśli zrobią to samo na obu platformach.
Te rozwiązania muszą zostać zapisane w plikach z
\r\n
podziałem wiersza, w przeciwnym razie nie będą działaćcmd
.źródło
Poprzednie odpowiedzi wydają się obejmować prawie wszystkie opcje i bardzo mi pomogły. Dołączam tę odpowiedź tutaj, aby zademonstrować mechanizm, którego użyłem do włączenia zarówno skryptu Bash, jak i skryptu CMD systemu Windows w tym samym pliku.
LinuxWindowsScript.bat
Podsumowanie
W systemie Linux
Pierwsza linia (
echo >/dev/null # >nul & GOTO WINDOWS & rem ^
) zostanie zignorowana, a skrypt będzie przepływał przez każdą następną bezpośrednio po niej linię, ażexit 0
polecenie zostanie wykonane. Poexit 0
osiągnięciu wykonywania skryptu zakończy się, ignorując polecenia systemu Windows poniżej.W systemie Windows
Pierwsza linia wykona
GOTO WINDOWS
polecenie, pomijając polecenia Linuksa bezpośrednio następujące po nim i kontynuując wykonywanie w:WINDOWS
linii.Usuwanie zwrotów karetki w systemie Windows
Ponieważ edytowałem ten plik w systemie Windows, musiałem systematycznie usuwać znaki powrotu karetki (\ r) z poleceń Linuksa, bo inaczej otrzymałem nieprawidłowe wyniki podczas uruchamiania części Bash. Aby to zrobić, otworzyłem plik w Notepad ++ i wykonałem następujące czynności:
Włącz opcję do wyświetlania znaków końca wiersza (
View
>Show Symbol
>Show End of Line
). Powroty karetki będą wtedy wyświetlane jakoCR
znaki.Wykonaj polecenie Znajdź i zamień (
Search
>Replace...
) i zaznaczExtended (\n, \r, \t, \0, \x...)
opcję.Wpisz
\r
wFind what :
polu i puste na zewnątrzReplace with :
pola, więc nie ma nic w nim.Zaczynając od góry pliku, klikaj
Replace
przycisk, aż wszystkie znaki powrotu karetki (CR
) zostaną usunięte z górnej części linuksowej. Pamiętaj, aby zostawić znaki powrotu karetki (CR
) dla części Windows.Wynik powinien być taki, że każde polecenie Linuksa kończy się tylko znakiem wysuwu wiersza (
LF
), a każde polecenie systemu Windows znakiem powrotu karetki i wysuwu wiersza (CR
LF
).źródło
Używam tej techniki do tworzenia uruchamialnych plików jar. Ponieważ plik jar / zip zaczyna się w nagłówku zip, mogę umieścić uniwersalny skrypt, aby uruchomić ten plik na górze:
@
in sh zgłasza błąd, który jest przesyłany potokiem do,/dev/null
a następnie rozpoczyna się komentarz. W cmd potok/dev/null
kończy się niepowodzeniem, ponieważ plik nie jest rozpoznawany w systemie Windows, ale ponieważ system Windows nie wykrywa#
jako komentarza, błąd jest przesyłany potokiemnul
. Następnie wyłącza echo. Ponieważ cała linia jest poprzedzona@
znakiem, to nie dostaje printet w cmd.::
, który rozpoczyna komentarz w cmd, do noop w sh. Ma to tę zaletę, że::
nie resetuje się$?
do0
. Używa:;
sztuczki „ jest etykietą”.::
i są one ignorowane w cmd:: exit
zakończeniu skryptu sh i mogę pisać polecenia cmdcommand not found
. Musisz sam zdecydować, czy tego potrzebujesz, czy nie.źródło
Potrzebowałem tego dla niektórych moich skryptów instalacyjnych pakietu Python. Większość rzeczy między plikiem sh i bat jest taka sama, ale kilka rzeczy, takich jak obsługa błędów, jest różnych. Można to zrobić w następujący sposób:
Następnie wywołujesz to ze skryptu bash:
Plik wsadowy systemu Windows wygląda następująco:
źródło
Wypróbuj mój projekt BashWin na https://github.com/skanga/bashwin, który używa BusyBox dla większości poleceń Unix
źródło
Istnieją niezależne od platformy narzędzia do budowania, takie jak Ant lub Maven ze składnią xml (oparte na Javie). Możesz więc przepisać wszystkie swoje skrypty w Ant lub Maven i uruchomić je niezależnie od typu systemu operacyjnego. Możesz też po prostu utworzyć skrypt opakowujący Ant, który przeanalizuje typ systemu operacyjnego i uruchomi odpowiedni skrypt bat lub bash.
źródło