Un seul script à exécuter dans Windows batch et Linux Bash?

Est-il possible d’écrire un seul fichier de script qui s’exécute dans Windows (traité comme .bat) et Linux (via Bash)?

Je connais la syntaxe de base des deux, mais je n’ai pas compris. Il pourrait probablement exploiter la syntaxe obscure de Bash ou certains problèmes de processeur de lots Windows.

La commande à exécuter peut être une simple ligne pour exécuter un autre script.

La motivation est d’avoir une seule commande de démarrage d’application pour Windows et Linux.

Mise à jour: Le script shell “natif” du système a besoin de choisir la bonne version d’interpréteur, de se conformer à certaines variables d’environnement bien connues, etc. L’installation d’environnements supplémentaires tels que CygWin n’est pas préférable télécharger et exécuter “.

Le seul autre langage à prendre en compte pour Windows est Windows Scripting Host – WSH, qui est prédéfini par défaut depuis 98.

Ce que j’ai fait, c’est utiliser la syntaxe d’étiquette de cmd comme marqueur de commentaire . Le caractère label, deux-points (:), équivaut à true dans la plupart des shells POSIXish. Si vous suivez immédiatement le caractère de libellé par un autre caractère qui ne peut pas être utilisé dans un GOTO , le fait de commenter votre script cmd ne doit pas affecter votre code cmd .

Le hack est de mettre des lignes de code après la séquence de caractères “ :; ”. Si vous écrivez principalement des scripts à une ligne ou, comme cela peut être le cas, vous pouvez écrire une ligne de sh pour plusieurs lignes de cmd , les éléments suivants pourraient convenir. N’oubliez pas que toute utilisation de $? doit être avant votre prochain point : parce que : réinitialise $? à 0

 :; echo "Hi, I'm ${SHELL}."; exit $? @ECHO OFF ECHO I'm %COMSPEC% 

Un exemple très inventé de gardiennage $? :

 :; false; ret=$? :; [ ${ret} = 0 ] || { echo "Program failed with code ${ret}." >&2; exit 1; } :; exit ECHO CMD code. 

Une autre idée pour ignorer le code cmd est d’utiliser heredocs afin que sh traite le code cmd comme une chaîne inutilisée et cmd interprète. Dans ce cas, nous nous assurons que le délimiteur de notre heredoc est à la fois cité (pour empêcher sh d’interpréter son contenu lors de son exécution avec sh ) et commence par : afin que cmd saute comme toute autre ligne commençant par:.

 :; echo "I am ${SHELL}" :< <"::CMDLITERAL" ECHO I am %COMSPEC% ::CMDLITERAL :; echo "And ${SHELL} is back!" :; exit ECHO And back to %COMSPEC% 

En fonction de vos besoins ou de votre style de codage, l’entrelacement des codes cmd et sh peut ou non avoir un sens. Utiliser heredocs est une méthode pour effectuer un tel entrelacement. Cela pourrait cependant être étendu avec la technique GOTO :

 :< <"::CMDLITERAL" @ECHO OFF GOTO :CMDSCRIPT ::CMDLITERAL echo "I can write free-form ${SHELL} now!" if :; then echo "This makes conditional constructs so much easier because" echo "they can now span multiple lines." fi exit $? :CMDSCRIPT ECHO Welcome to %COMSPEC% 

Les commentaires universels, bien sûr, peuvent être faits avec la séquence de caractères : # ou :;# . L'espace ou le point-virgule sont nécessaires car sh considère # comme faisant partie d'un nom de commande s'il ne s'agit pas du premier caractère d'un identifiant. Par exemple, vous pouvez écrire des commentaires universels sur les premières lignes de votre fichier avant d'utiliser la méthode GOTO pour fractionner votre code. Ensuite, vous pouvez informer votre lecteur de la raison pour laquelle votre script est écrit de manière si étrange:

 : # This is a special script which intermixes both sh : # and cmd code. It is written this way because it is : # used in system() shell-outs directly in otherwise : # portable code. See https://stackoverflow.com/questions/17510688 : # for details. :; echo "This is ${SHELL}"; exit @ECHO OFF ECHO This is %COMSPEC% 

Ainsi, certaines idées et méthodes pour réaliser des scripts compatibles sh et cmd sans effets secondaires graves à ma connaissance (et sans que la sortie cmd '#' is not recognized as an internal or external command, operable program or batch file. ).

vous pouvez essayer ceci:

 #|| goto :batch_part echo $PATH #exiting the bash part exit :batch_part echo %PATH% 

Vous aurez probablement besoin d’utiliser /r/n comme nouvelle ligne au lieu d’un style unix. Si je me souviens bien, la nouvelle ligne unix n’est pas interprétée comme une nouvelle ligne par les scripts .bat Une autre méthode consiste à créer un #.exe fichier dans le chemin qui ne fait rien de la même manière que ma réponse ici: Est-il possible d’intégrer et d’exécuter VBScript dans un fichier de commandes sans utiliser de fichier temporaire?

MODIFIER

La réponse de binki est presque parfaite mais peut encore être améliorée:

 :<  

Il utilise à nouveau le : sortingck et le commentaire multi-ligne. Regarde comme cmd.exe (au moins sur Windows 10) fonctionne sans problèmes avec les EOL de style unix alors assurez-vous que votre script est converti au format Linux. (la même approche a déjà été utilisée ici et ici ). Bien que l’utilisation de shebang produise encore une production redondante ...

Je voulais commenter, mais je peux seulement append une réponse pour le moment.

Les techniques données sont excellentes et je les utilise également.

Il est difficile de conserver un fichier contenant deux types de sauts de ligne, à savoir /n pour la partie bash et /r/n pour la partie Windows. La plupart des éditeurs essayent d’appliquer un schéma de saut de ligne commun en devinant le type de fichier que vous éditez. De plus, la plupart des méthodes de transfert du fichier sur Internet (en particulier sous forme de texte ou de fichier de script) blanchiront les sauts de ligne. Vous pouvez donc commencer avec un type de saut de ligne et vous retrouver avec l’autre. Si vous avez formulé des hypothèses sur les sauts de ligne et que vous avez ensuite donné votre script à quelqu’un d’autre, il se peut que cela ne fonctionne pas pour eux.

L’autre problème concerne les systèmes de fichiers montés en réseau (ou CD) qui sont partagés entre différents types de systèmes (en particulier lorsque vous ne pouvez pas contrôler les logiciels disponibles pour l’utilisateur).

Il faut donc utiliser le saut de ligne DOS de /r/n et protéger également le script bash du DOS /r en mettant un commentaire à la fin de chaque ligne ( # ). Vous ne pouvez pas non plus utiliser les continuations de ligne dans bash car le /r fera casser.

De cette façon, quiconque utilise le script et quel que soit l’environnement, cela fonctionnera.

J’utilise cette méthode en conjonction avec la fabrication de Makefiles portables!

Ce qui suit fonctionne pour moi sans aucune erreur ou message d’erreur avec Bash 4 et Windows 10, contrairement aux réponses ci-dessus. Je nomme le fichier “importe.cmd”, fais chmod +x pour le rendre exécutable dans linux, et fais en sorte qu’il ait des fins de ligne unix ( dos2unix ) pour garder dos2unix bash.

 :; if [ -z 0 ]; then @echo off goto :WINDOWS fi if [ -z "$2" ]; then echo "usage: $0  " exit 1 fi # bash stuff exit :WINDOWS if [%2]==[] ( SETLOCAL enabledelayedexpansion set usage="usage: %0  " @echo !usage:"=! exit /b 1 ) :: windows stuff 

Il existe plusieurs manières d’exécuter différentes commandes sur bash et cmd avec le même script.

cmd ignorera les lignes qui commencent par :; , comme mentionné dans d’autres réponses. Il ignorera également la ligne suivante si la ligne en cours se termine par la commande rem ^ , car le caractère ^ échappera au saut de ligne et la ligne suivante sera traitée comme un commentaire par rem .

Comme pour faire bash ignorer les lignes de cmd , il y a plusieurs façons. J’ai énuméré quelques façons de le faire sans casser les commandes cmd :

Commande inexistante # (non recommandée)

Si aucune commande # n’est disponible sur cmd lors de l’exécution du script, nous pouvons le faire:

 # 2>nul & echo Hello cmd! & rem ^ echo 'Hello bash!' # 

Le caractère # au début de la ligne cmd fait que bash traite cette ligne comme un commentaire.

Le caractère # à la fin de la ligne bash est utilisé pour commenter le caractère, comme Brian Tompsett l’a souligné dans sa réponse . Sans cela, bash lancera une erreur si le fichier a des fins de ligne \r\n , requirejses par cmd .

En faisant # 2>nul , nous incitons cmd à ignorer l’erreur d’une commande # inexistante, tout en exécutant la commande suivante.

N’utilisez pas cette solution s’il existe une commande # sur le PATH ou si vous n’avez aucun contrôle sur les commandes disponibles pour cmd .


Utiliser echo pour ignorer le caractère # sur cmd

Nous pouvons utiliser echo avec sa sortie redirigée pour insérer des commandes cmd sur la zone commentée de bash :

 echo >/dev/null # >nul & echo Hello cmd! & rem ^ echo 'Hello bash!' # 

Puisque le caractère # n’a aucune signification particulière sur cmd , il est traité comme une partie du texte à echo . Il suffit de redirect la sortie de la commande echo et d’y insérer d’autres commandes.


Vider #.bat fichier #.bat

 echo >/dev/null # 1>nul 2> #.bat # & echo Hello cmd! & del #.bat & rem ^ echo 'Hello bash!' # 

La ligne echo >/dev/null # 1>nul 2> #.bat crée un fichier #.bat vide lorsque vous cmd sur cmd (ou remplace le fichier #.bat existant, le cas échéant), et ne fait rien sur bash .

Ce fichier sera utilisé par les lignes de commandes suivantes, même s’il existe une autre commande # sur le PATH .

La commande del #.bat du code spécifique à cmd supprime le fichier créé. Vous n’avez qu’à le faire sur la dernière ligne de cmd .

N’utilisez pas cette solution si un fichier #.bat peut se trouver dans votre répertoire de travail actuel, car ce fichier sera effacé.


Recommandé: utiliser here-document pour ignorer les commandes cmd sur bash

 :; echo 'Hello bash!';< <: echo Hello cmd! & ^ : 

En plaçant le caractère ^ à la fin de la ligne cmd , nous échappons au saut de ligne et, en utilisant : comme délimiteur ici-document, le contenu de la ligne de délimitation n'aura aucun effet sur cmd . De cette façon, cmd n'exécutera sa ligne qu'après la fin de la ligne : ayant le même comportement que bash .

Si vous voulez avoir plusieurs lignes sur les deux plates-formes et ne les exécutez qu'à la fin du bloc, vous pouvez le faire:

 :;( # :; echo 'Hello' # :; echo 'bash!' # :; );< <'here-document delimiter' ( echo Hello echo cmd! ) & rem ^ here-document delimiter 

Tant qu'il n'y a pas de ligne de commande avec exactement le here-document delimiter , cette solution devrait fonctionner. Vous pouvez changer le here-document delimiter en n'importe quel autre texte.


Dans toutes les solutions présentées, les commandes ne seront exécutées qu'après la dernière ligne , rendant leur comportement cohérent si elles font la même chose sur les deux plates-formes.

Ces solutions doivent être enregistrées dans des fichiers avec \r\n comme sauts de ligne, sinon ils ne fonctionneront pas sur cmd .

Les réponses précédentes semblent couvrir à peu près toutes les options et m’ont beaucoup aidé. J’inclus cette réponse ici pour illustrer le mécanisme que j’ai utilisé pour inclure à la fois un script Bash et un script Windows CMD dans le même fichier.

LinuxWindowsScript.bat

 echo >/dev/null # >nul & GOTO WINDOWS & rem ^ echo 'Processing for Linux' # *********************************************************** # * NOTE: If you modify this content, be sure to remove carriage returns (\r) # * from the Linux part and leave them in together with the line feeds # * (\n) for the Windows part. In summary: # * New lines in Linux: \n # * New lines in Windows: \r\n # *********************************************************** # Do Linux Bash commands here... for example: StartDir="$(pwd)" # Then, when all Linux commands are complete, end the script with 'exit'... exit 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :WINDOWS echo "Processing for Windows" REM Do Windows CMD commands here... for example: SET StartDir=%cd% REM Then, when all Windows commands are complete... the script is done. 

Résumé

Sous Linux

La première ligne ( echo >/dev/null # >nul & GOTO WINDOWS & rem ^ ) sera ignorée et le script passera par chaque ligne qui le suit jusqu’à l’exécution de la commande exit 0 . Une fois la exit 0 atteinte, l’exécution du script s’achève, ignorant les commandes Windows situées en dessous.

Dans Windows

La première ligne exécutera la commande GOTO WINDOWS , ignorant les commandes Linux qui la suivent immédiatement et poursuivant l’exécution à la ligne :WINDOWS .

Suppression des retours de chariot dans Windows

Comme je modifiais ce fichier sous Windows, je devais supprimer systématiquement les retours chariot (\ r) des commandes Linux ou des résultats anormaux lors de l’exécution de la partie Bash. Pour ce faire, j’ai ouvert le fichier dans Notepad ++ et ai fait ce qui suit:

  1. Activez l’option d’affichage des caractères de fin de ligne ( View > Show Symbol > Show End of Line ). Les retours de chariot seront alors affichés sous forme de caractères CR .

  2. Effectuez une recherche et remplacement ( Search > Replace... ) et cochez l’option Extended (\n, \r, \t, \0, \x...) .

  3. Tapez \r dans le champ Find what : et videz le champ Replace with : afin qu’il n’y ait rien.

  4. En commençant par le haut du fichier, cliquez sur le bouton Replace jusqu’à ce que tous les caractères de retour chariot aient été supprimés de la partie Linux supérieure. Veillez à laisser les caractères de retour chariot ( CR ) pour la partie Windows.

Le résultat devrait être que chaque commande Linux se termine par un LF ligne ( LF ) et que chaque commande Windows se termine par un retour chariot et un LF ligne ( CR LF ).

Vous pouvez partager des variables:

 :;SET() { eval $1; } SET var=value :;echo $var :;exit ECHO %var% 

J’utilise cette technique pour créer des fichiers jar exécutables. Étant donné que le fichier jar / zip commence à l’en-tête zip, je peux mettre un script universel pour exécuter ce fichier en haut:

 #!/usr/bin/env sh @ 2>/dev/null # 2>nul & echo off :; alias ::='' :: exec java -jar $JAVA_OPTS "$0" "$@" :: exit java -jar %JAVA_OPTS% "%~dpnx0" %* exit /B 
  • La première ligne échoue dans cmd et n’imprime rien sur sh. Cela est dû au fait que le @ in sh renvoie une erreur qui est transmise à /dev/null et après quoi un commentaire commence. Sur cmd, le canal vers /dev/null échoue car le fichier n’est pas reconnu sur Windows mais comme Windows ne détecte pas # comme commentaire, l’erreur est renvoyée à nul . Ensuite, il fait un écho. Parce que la ligne entière est précédée par un @ elle ne reçoit pas printet sur cmd.
  • Le second définit :: , qui lance un commentaire dans cmd, à noop dans sh. Cela a l’avantage que :: ne réinitialise pas $? à 0 Il utilise l’astuce ” :; est une étiquette”.
  • Maintenant, je peux append des commandes sh avec :: et elles sont ignorées dans cmd
  • On :: exit le script sh se termine et je peux écrire des commandes cmd
  • Seule la première ligne (shebang) est problématique en cmd car elle imprimera la command not found . Vous devez décider vous-même si vous en avez besoin ou non.

Il existe une plate-forme indépendante des outils de construction tels que Ant ou Maven avec la syntaxe XML (basée sur Java). Donc, vous pouvez réécrire tous vos scripts dans Ant ou Maven et les exécuter malgré le type d’OS. Ou vous pouvez simplement créer un script de wrapper Ant, qui parsingra os type et exécutera le script bat ou bash approprié.