scripts script resueltos programas programa paso manejo hacer ejercicios ejemplos ejecutar desde como cadenas bash batch-file

resueltos - scripts bash ejemplos



¿Script único para ejecutar tanto en lote de Windows como en Linux Bash? (9)

¿Es posible escribir un único archivo de script que se ejecute tanto en Windows (tratado como .bat) como en Linux (a través de Bash)?

Conozco la sintaxis básica de ambos, pero no me di cuenta. Probablemente podría explotar la sintaxis oscura de Bash o algún error en el procesador de lotes de Windows.

El comando para ejecutar puede ser solo una línea para ejecutar otro script.

La motivación es tener solo un comando de inicio de aplicación único para Windows y Linux.

Actualización: la necesidad del script de shell "nativo" del sistema es que necesita elegir la versión del intérprete adecuada, ajustarse a ciertas variables de entorno conocidas, etc. Instalar entornos adicionales como CygWin no es preferible. Me gustaría mantener el concepto ". descargar y ejecutar ".

El único otro lenguaje a considerar para Windows es Windows Scripting Host - WSH, que está predeterminado de forma predeterminada desde 98.


Hay una plataforma independiente de herramientas de compilación como Ant o Maven con sintaxis xml (basada en Java). Entonces, podrías reescribir todos tus scripts en Ant o Maven para ejecutarlos a pesar del tipo de os. O simplemente podría crear una secuencia de comandos Ant wrapper, que analizará el tipo de sistema operativo y ejecutará las secuencias de comandos bat o bash apropiadas.


Hay varias maneras de ejecutar comandos diferentes en bash y cmd con el mismo script.

cmd ignorará las líneas que comienzan con :; , como se menciona en otras respuestas. Ignorará también la siguiente línea si la línea actual finaliza con el comando rem ^ , ya que el carácter ^ escapará del salto de línea y la siguiente línea se tratará como un comentario de rem .

En cuanto a hacer que bash ignore las líneas de cmd , hay varias formas. He enumerado algunas formas de hacerlo sin romper los comandos cmd :

Comando # inexistente (no recomendado)

Si no hay ningún comando # disponible en cmd cuando se ejecuta el script, podemos hacer esto:

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

El carácter # al principio de la línea de cmd hace que bash trate esa línea como un comentario.

El carácter # al final de la línea bash se usa para comentar el carácter /r , como señaló Brian Tompsett en su respuesta . Sin esto, bash arrojará un error si el archivo tiene /r/n terminaciones de línea, requeridas por cmd .

Al hacer # 2>nul , estamos engañando a cmd para que ignore el error de un comando # inexistente, mientras se sigue ejecutando el comando que sigue.

No use esta solución si hay un comando # disponible en la PATH o si no tiene control sobre los comandos disponibles para cmd .

Usar echo para ignorar el carácter # en cmd

Podemos usar echo con su salida redirigida para insertar comandos cmd en el área comentada de bash :

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

Como el carácter # no tiene un significado especial en cmd , se trata como una parte del texto para hacer echo . Todo lo que teníamos que hacer era redirigir la salida del comando echo e insertar otros comandos después.

Archivo vacío #.bat

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

La línea echo >/dev/null # 1>nul 2> #.bat crea un archivo #.bat vacío mientras está en cmd (o reemplaza el #.bat existente, si corresponde), y no hace nada mientras está en bash .

Este archivo será utilizado por las líneas de cmd que siguen, incluso si hay algún otro comando # en la PATH .

El comando del #.bat en el código específico de cmd elimina el archivo que se creó. Solo tienes que hacer esto en la última línea de cmd .

No use esta solución si un archivo #.bat podría estar en su directorio de trabajo actual, ya que ese archivo se borrará.

Recomendado: usando heredocs para ignorar los comandos de cmd en bash

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

Al colocar el carácter ^ al final de la línea de cmd , estamos escapando del salto de línea, y al usar : como el delimitador de documento aquí, el contenido de la línea de delimitador no tendrá efecto en cmd . De esta forma, cmd solo ejecutará su línea después de que : línea haya terminado, teniendo el mismo comportamiento que bash .

Si desea tener varias líneas en ambas plataformas y solo ejecutarlas al final del bloque, puede hacer esto:

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

Mientras no haya una línea de cmd con el here-document delimiter , esta solución debería funcionar. Puede cambiar here-document delimiter a cualquier otro texto.

En todas las soluciones presentadas, los comandos solo se ejecutarán después de la última línea , haciendo que su comportamiento sea consistente si hacen lo mismo en ambas plataformas.

Esas soluciones se deben guardar en archivos con /r/n como saltos de línea, de lo contrario no funcionarán en cmd .


Las respuestas anteriores parecen cubrir prácticamente todas las opciones y me ayudaron mucho. Incluyo esta respuesta aquí solo para demostrar el mecanismo que solía incluir tanto un script Bash como un script CMD de Windows en el mismo archivo.

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.

Resumen

En Linux

La primera línea ( echo >/dev/null # >nul & GOTO WINDOWS & rem ^ ) será ignorada y la secuencia de comandos fluirá a través de cada línea inmediatamente después hasta que se ejecute el comando exit 0 . Una vez que se llega a la exit 0 , la ejecución del script finalizará, ignorando los comandos de Windows que se encuentran debajo.

En Windows

La primera línea ejecutará el comando GOTO WINDOWS , omitiendo los comandos de Linux que lo siguen inmediatamente y continuando la ejecución en la línea :WINDOWS .

Eliminar devoluciones de carro en Windows

Dado que estaba editando este archivo en Windows, tuve que eliminar sistemáticamente los retornos de carro (/ r) de los comandos de Linux o de lo contrario obtuve resultados anormales al ejecutar la porción de Bash. Para hacer esto, abrí el archivo en Notepad++ e hice lo siguiente:

  1. Active la opción para ver los caracteres de fin de línea ( View > Show Symbol > Show End of Line ). Los retornos de carro se mostrarán como caracteres CR .

  2. Haga una búsqueda y reemplazo ( Search > Replace... ) y marque la opción Extended (/n, /r, /t, /0, /x...) .

  3. Escriba /r en el campo Find what : y elimine el campo Replace with : para que no haya nada en él.

  4. Comenzando en la parte superior del archivo, haga clic en el botón Replace hasta que todos los caracteres de retorno de carro ( CR ) se hayan eliminado de la parte superior de Linux. Asegúrese de dejar los caracteres de retorno de carro ( CR ) para la porción de Windows.

El resultado debería ser que cada comando de Linux termina en solo un avance de línea ( LF ) y cada comando de Windows termina en un retorno de carro y avance de línea ( CR LF ).


Lo que he hecho es usar la sintaxis de la etiqueta de cmd como marcador de comentarios . El carácter de etiqueta, dos puntos (:), es equivalente a true en la mayoría de las shells POSIX. Si inmediatamente sigues el carácter de la etiqueta por otro personaje que no puede usarse en un GOTO , comentar tu script cmd no debería afectar tu código cmd .

El truco consiste en poner líneas de código después de la secuencia de caracteres " :; ". Si está escribiendo sobre todo guiones de una sola línea o, como puede ser el caso, puede escribir una línea de sh para muchas líneas de cmd , lo siguiente podría estar bien. No olvides que cualquier uso de $? debe estar antes de su próximo colón : porque : restablece $? a 0

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

¿Un ejemplo muy artificioso de guardar $? :

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

Otra idea para omitir el código cmd es usar heredocs para que sh trate el código cmd como una cadena no utilizada y cmd interpreta. En este caso, nos aseguramos de que el delimitador de nuestro heredoc sea citado (para evitar que sh haga cualquier tipo de interpretación sobre su contenido cuando se ejecuta con sh ) y comienza con : para que cmd salte como cualquier otra línea que comience con :

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

Dependiendo de sus necesidades o estilo de codificación, el código cmd y sh entrelazado puede o no tener sentido. El uso de heredocs es un método para realizar dicho entrelazado. Sin embargo, esto podría extenderse con la técnica 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%

Los comentarios universales, por supuesto, se pueden hacer con la secuencia de caracteres : # o :;# . El espacio o punto y coma son necesarios porque sh considera que # es parte de un nombre de comando si no es el primer carácter de un identificador. Por ejemplo, es posible que desee escribir comentarios universales en las primeras líneas de su archivo antes de usar el método GOTO para dividir su código. Luego puede informar a su lector de por qué su guión está escrito de forma tan extraña:

: # 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://.com/questions/17510688 : # for details. :; echo "This is ${SHELL}"; exit @ECHO OFF ECHO This is %COMSPEC%

Por lo tanto, algunas ideas y formas de realizar scripts compatibles con sh y cmd sin graves efectos secundarios hasta donde yo sé (y sin tener cmd output ''#'' is not recognized as an internal or external command, operable program or batch file. ).


Lo siguiente funciona para mí sin ningún error o mensaje de error con Bash 4 y Windows 10, a diferencia de las respuestas anteriores. Puse nombre al archivo "whatever.cmd", hago chmod +x para que sea ejecutable en Linux, y lo hago tener terminaciones de línea Unix ( dos2unix ) para mantener bash quiet.

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


Puedes compartir variables:

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


Quería comentar, pero solo puedo agregar una respuesta en este momento.

Las técnicas dadas son excelentes y las uso también.

Es difícil retener un archivo que contiene dos tipos de saltos de línea, siendo /n para la parte bash y /r/n para la parte windows. La mayoría de los editores intentan aplicar un esquema de salto de línea común adivinando qué tipo de archivo está editando. Además, la mayoría de los métodos de transferencia del archivo a través de Internet (particularmente como un archivo de texto o script) lavará los saltos de línea, por lo que podría comenzar con un tipo de salto de línea y terminar con el otro. Si hizo suposiciones acerca de los saltos de línea y luego le dio el guión a otra persona para que lo use, es posible que encuentre que no funciona para ellos.

El otro problema son los sistemas de archivos montados en red (o CD) que se comparten entre diferentes tipos de sistema (particularmente donde no se puede controlar el software disponible para el usuario).

Por lo tanto, uno debe usar el salto de línea DOS de /r/n y también proteger el script bash del DOS /r poniendo un comentario al final de cada línea ( # ). Tampoco puede usar las continuidades de línea en bash porque /r hará que se rompan.

De esta forma, quien use el script y en cualquier entorno, funcionará.

¡Utilizo este método junto con Makefiles portátiles!


Utilizo esta técnica para crear archivos jar ejecutables. Como el archivo jar / zip se inicia en el encabezado zip, puedo poner un script universal para ejecutar este archivo en la parte superior:

#!/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 primera línea hace eco en cmd y no imprime nada en sh. Esto se debe a que @ in sh arroja un error que se canaliza a /dev/null y luego comienza un comentario. En cmd, el conducto a /dev/null falla porque el archivo no se reconoce en Windows, pero dado que Windows no detecta # como comentario, el error se canaliza a nul . Luego hace un eco apagado. Como toda la línea está precedida por un @ , no obtiene printet en cmd.
  • El segundo define :: , que comienza un comentario en cmd, a noop en sh. Esto tiene el beneficio de que :: no restablece $? a 0 Utiliza el truco " :; es una etiqueta".
  • Ahora puedo anteponer comandos sh con :: y se ignoran en cmd
  • On :: exit termina el script sh y puedo escribir comandos cmd
  • Solo la primera línea (shebang) es problemática en cmd ya que imprimirá el command not found . Tienes que decidirte a ti mismo si lo necesitas o no.

puedes probar esto:

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

Probablemente necesitará usar /r/n como una nueva línea en lugar de un estilo de Unix. Si mal no recuerdo, la nueva línea de Unix no se interpreta como una nueva línea mediante .bat Otra forma es crear un #.exe archivo en la ruta que no hace nada de manera similar a mi respuesta aquí: ¿Es posible incrustar y ejecutar VBScript dentro de un archivo por lotes sin usar un archivo temporal?

EDITAR

La respuesta del binki es casi perfecta, pero aún se puede mejorar:

:<<BATCH @echo off echo %PATH% exit /b BATCH echo $PATH

Utiliza de nuevo el truco y el comentario de varias líneas. Parece que cmd.exe (al menos en windows10) funciona sin problemas con los EOL de estilo de Unix, así que asegúrese de que su script se convierta en formato de Linux. (Se ha visto el mismo enfoque usado antes here y here ). Aunque usar shebang todavía producirá salida redundante ...