Pertanyaan Skrip tunggal untuk berjalan di kedua batch Windows dan Linux Bash?


Apakah mungkin untuk menulis file skrip tunggal yang dijalankan di kedua Windows (diperlakukan sebagai .bat) dan Linux (melalui Bash)?

Saya tahu sintaks dasar keduanya, tetapi tidak tahu. Itu mungkin bisa mengeksploitasi beberapa sintaks tidak jelas Bash atau beberapa kesalahan prosesor batch Windows.

Perintah untuk mengeksekusi mungkin hanya satu baris untuk mengeksekusi skrip lainnya.

Motivasi adalah untuk memiliki hanya satu perintah boot aplikasi untuk Windows dan Linux.

Memperbarui: Kebutuhan untuk skrip shell "asli" sistem adalah bahwa ia perlu memilih versi penerjemah yang tepat, sesuai dengan variabel lingkungan terkenal tertentu, dll. Menginstal lingkungan tambahan seperti CygWin tidak disukai - Saya ingin menyimpan konsep "unduh & amp; menjalankan".

Satu-satunya bahasa lain yang dipertimbangkan untuk Windows adalah Windows Scripting Host - WSH, yang ditetapkan secara default sejak 98.


75
2017-07-07 09:05


asal


Jawaban:


Apa yang saya lakukan adalah gunakan sintaks label cmd sebagai penanda komentar. Karakter label, tanda titik dua (:), setara dengan true di sebagian besar kerang POSIXish. Jika Anda segera mengikuti karakter label oleh karakter lain yang tidak dapat digunakan dalam GOTO, lalu mengomentari Anda cmd skrip tidak boleh mempengaruhi Anda cmd kode.

Peretasan ini untuk menempatkan baris kode setelah urutan karakter “:;". Jika Anda menulis sebagian besar skrip satu baris atau, mungkin saja, dapat menulis satu baris sh untuk banyak baris cmd, berikut ini mungkin baik-baik saja. Jangan lupa bahwa ada gunanya $? harus sebelum usus besar Anda berikutnya : karena : ulang $? ke 0.

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

Contoh yang sangat dibikin untuk menjaga $?:

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

Ide lain untuk melompati cmd kode digunakan heredocs maka sh memperlakukan cmd kode sebagai string yang tidak digunakan dan cmd menafsirkannya. Dalam hal ini, kami memastikan bahwa pemisah heredoc kami berdua dikutip (untuk berhenti sh dari melakukan penafsiran apa pun pada isinya saat menjalankannya sh) dan dimulai dengan : maka cmd melompati itu seperti garis lain yang dimulai dengan :.

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

Tergantung pada kebutuhan Anda atau gaya pengkodean, interlacing cmd dan sh kode mungkin atau mungkin tidak masuk akal. Menggunakan heredocs adalah salah satu metode untuk melakukan interlace tersebut. Ini bisa, bagaimanapun, diperpanjang dengan GOTO teknik:

:<<"::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%

Komentar universal, tentu saja, bisa dilakukan dengan urutan karakter : # atau :;#. Ruang atau titik koma diperlukan karena sh pertimbangkan # menjadi bagian dari nama komando jika bukan karakter pertama dari identifier. Misalnya, Anda mungkin ingin menulis komentar universal di baris pertama file Anda sebelum menggunakan GOTO metode untuk membagi kode Anda. Kemudian Anda dapat memberi tahu pembaca tentang mengapa skrip Anda ditulis dengan sangat aneh:

: # 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%

Dengan demikian, beberapa gagasan dan cara mencapainya sh dan cmdskrip-kompatibel tanpa efek samping yang serius sejauh yang saya tahu (dan tanpa cmdkeluaran '#' is not recognized as an internal or external command, operable program or batch file.).


65
2017-07-12 20:44



Anda dapat mencoba ini:

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

Mungkin Anda harus menggunakannya /r/n sebagai baris baru, bukan gaya unix. Jika saya ingat benar, baris baru unix tidak ditafsirkan sebagai baris baru oleh .bat skrip. Cara lain adalah membuat #.exe file di jalur yang tidak melakukan apa-apa dengan cara yang sama seperti jawaban saya di sini: Apakah mungkin untuk menanamkan dan menjalankan VBScript dalam file batch tanpa menggunakan file sementara?

EDIT

Itu Jawaban binki hampir sempurna tetapi masih bisa ditingkatkan:

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

echo $PATH

Ini menggunakan lagi : trik dan komentar multi baris. Terlihat seperti cmd.exe (paling tidak pada windows10) bekerja tanpa masalah dengan EOL gaya unix jadi pastikan skrip Anda diubah ke dalam format linux. (Pendekatan yang sama telah terlihat digunakan sebelumnya sini dan sini ). Meskipun menggunakan shebang masih akan menghasilkan output yang berlebihan ...


16
2017-07-07 09:29



Saya ingin berkomentar, tetapi hanya dapat menambahkan jawaban saat ini.

Teknik yang diberikan sangat bagus dan saya menggunakannya juga.

Sulit untuk menyimpan file yang memiliki dua jenis jeda baris yang terdapat di dalamnya, yang ada /n untuk bagian bash dan /r/n untuk bagian jendela. Kebanyakan editor mencoba dan menegakkan skema peretasan baris umum dengan menebak jenis file yang Anda edit. Juga sebagian besar metode mentransfer file di internet (terutama sebagai file teks atau skrip) akan mencuci jeda baris, sehingga Anda bisa mulai dengan satu jenis jeda baris dan berakhir dengan yang lain. Jika Anda membuat asumsi tentang jeda baris dan kemudian memberikan skrip Anda kepada orang lain untuk digunakan, mereka mungkin menganggapnya tidak berfungsi untuk mereka.

Masalah lainnya adalah sistem file yang dipasang di jaringan (atau CD) yang dibagi antara jenis sistem yang berbeda (terutama di mana Anda tidak dapat mengontrol perangkat lunak yang tersedia untuk pengguna).

Oleh karena itu seseorang harus menggunakan istirahat baris DOS /r/n dan juga melindungi skrip bash dari DOS /r dengan memberi komentar di akhir setiap baris (#). Anda juga tidak dapat menggunakan garis lanjutan dalam bash karena /r akan menyebabkan mereka putus.

Dengan cara ini siapa pun yang menggunakan skrip, dan dalam lingkungan apa pun, ia akan bekerja.

Saya menggunakan metode ini bersama dengan membuat Makefile portabel!


10
2017-12-17 13:58



Berikut ini bekerja untuk saya tanpa kesalahan atau pesan kesalahan dengan Bash 4 dan Windows 10, tidak seperti jawaban di atas. Saya beri nama file "whatever.cmd", lakukan chmod +x untuk membuatnya dapat dijalankan di linux, dan membuatnya memiliki akhiran garis unix (dos2unix) untuk tetap tenang.

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

6
2017-08-19 04:24



Ada beberapa cara menjalankan perintah yang berbeda bash dan cmd dengan skrip yang sama.

cmd akan mengabaikan garis yang dimulai dengan :;, sebagaimana disebutkan dalam jawaban lainnya. Ini juga akan mengabaikan baris berikutnya jika baris saat ini diakhiri dengan perintah rem ^, seperti ^ karakter akan keluar dari jeda baris dan baris berikutnya akan diperlakukan sebagai komentar oleh rem.

Sedang untuk membuat bash abaikan cmd garis, ada beberapa cara. Saya telah menyebutkan beberapa cara untuk melakukannya tanpa melanggar cmdperintah:

Tidak ada # perintah (tidak disarankan)

Jika tidak ada # perintah tersedia cmd ketika skrip dijalankan, kita bisa melakukan ini:

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

Itu # karakter di awal cmd membuat garis bash perlakukan garis itu sebagai komentar.

Itu # karakter pada akhir bash baris digunakan untuk mengomentari \r karakter, seperti yang ditunjukkan Brian Tompsett jawabannya. Tanpa ini, bash akan menimbulkan kesalahan jika file tersebut ada \r\n akhiran garis, yang dibutuhkan oleh cmd.

Dengan melakukan # 2>nulkita menipu cmd untuk mengabaikan kesalahan dari beberapa tidak ada # perintah, sambil tetap menjalankan perintah yang mengikuti.

Jangan gunakan solusi ini jika ada # perintah tersedia di PATH atau jika Anda tidak memiliki kontrol atas perintah yang tersedia untuk cmd.


Menggunakan echo untuk mengabaikan # karakter aktif cmd

Kita bisa gunakan echo dengan itu output diarahkan untuk memasukkan cmd perintah aktif bash'S mengomentari area:

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

Sejak itu # karakter tidak memiliki arti khusus cmd, itu diperlakukan sebagai bagian dari teks echo. Yang harus kita lakukan adalah mengarahkan output dari echo perintah dan masukkan perintah lain setelahnya.


Kosong #.bat mengajukan

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

Itu echo >/dev/null # 1>nul 2> #.bat garis menciptakan kosong #.bat file saat aktif cmd (atau mengganti yang ada #.bat, jika ada), dan tidak melakukan apa pun saat aktif bash.

File ini akan digunakan oleh cmd baris (s) yang mengikuti bahkan jika ada beberapa lainnya # perintah pada PATH.

Itu del #.bat perintah pada cmd-kode spesifik menghapus file yang dibuat. Anda hanya perlu melakukan ini pada akhirnya cmd garis.

Jangan gunakan solusi ini jika a #.bat file bisa berada di direktori kerja Anda saat ini, karena file itu akan dihapus.


Direkomendasikan: menggunakan di sini-dokumen untuk mengabaikan cmd perintah aktif bash

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

Dengan menempatkan ^ karakter pada akhir cmd baris kami melarikan diri dari jeda baris, dan dengan menggunakan : sebagai pemisah di sini-dokumen, konten garis pembatas tidak akan berpengaruh cmd. Dengan begitu, cmd hanya akan mengeksekusi garisnya setelah : antrean selesai, memiliki perilaku yang sama dengan bash.

Jika Anda ingin memiliki beberapa baris di kedua platform dan hanya mengeksekusi mereka di akhir blok, Anda dapat melakukan ini:

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

Selama tidak ada cmd Sejalan dengan tepat here-document delimiter, solusi ini harus berfungsi. Anda bisa berubah here-document delimiter ke teks lainnya.


Di semua solusi yang disajikan, perintah hanya akan dieksekusi setelah baris terakhir, membuat perilaku mereka konsisten jika mereka melakukan hal yang sama pada kedua platform.

Solusi tersebut harus disimpan ke file dengan \r\n sebagai jeda baris, jika tidak, mereka tidak akan bekerja cmd.


2
2017-07-27 03:18



Anda dapat membagikan variabel:

:;SET() { eval $1; }

SET var=value

:;echo $var
:;exit
ECHO %var%

0
2017-10-04 12:44



Jawaban sebelumnya tampaknya mencakup hampir semua opsi dan banyak membantu saya. Saya termasuk jawaban ini di sini hanya untuk mendemonstrasikan mekanisme yang saya gunakan untuk menyertakan skrip Bash dan skrip Windows CMD dalam file yang sama.

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.

Ringkasan

Di Linux

Baris pertama (echo >/dev/null # >nul & GOTO WINDOWS & rem ^) akan diabaikan dan skrip akan mengalir melalui setiap baris segera mengikutinya sampai exit 0 perintah dijalankan. Sekali exit 0 tercapai, eksekusi skrip akan berakhir, mengabaikan perintah Windows di bawahnya.

Di Windows

Baris pertama akan mengeksekusi GOTO WINDOWS perintah, melewati perintah Linux segera mengikutinya dan melanjutkan eksekusi di :WINDOWS garis.

Menghapus Pengembalian Kereta di Windows

Karena saya mengedit file ini di Windows, saya harus secara sistematis menghapus carriage return (\ r) dari perintah Linux atau yang lain saya mendapat hasil abnormal ketika menjalankan bagian Bash. Untuk melakukan ini, saya membuka file Notepad ++ dan lakukan yang berikut:

  1. Aktifkan opsi untuk melihat karakter akhir baris (View> Show Symbol > Show End of Line). Pengembalian kereta akan ditampilkan sebagai CR karakter.

  2. Lakukan Cari & Ganti (Search > Replace...) dan periksa Extended (\n, \r, \t, \0, \x...) pilihan.

  3. Mengetik \r dalam Find what : lapangan dan kosongkan Replace with : lapangan jadi tidak ada apa-apa di dalamnya.

  4. Mulai dari bagian atas file, klik Replace tombol sampai semua carriage return (CR) karakter telah dihapus dari bagian Linux atas. Pastikan untuk meninggalkan pengembalian carriage (CR) karakter untuk bagian Windows.

Hasilnya seharusnya bahwa setiap perintah Linux berakhir hanya dengan feed baris (LF) dan setiap perintah Windows berakhir dengan pengereman dan umpan baris (CR  LF).


0
2018-02-22 22:46



Saya menggunakan teknik ini untuk membuat file jar runnable. Karena file jar / zip mulai di header zip, saya dapat menempatkan skrip universal untuk menjalankan file ini di bagian atas:

#!/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
  • Baris pertama bergema dalam cmd dan tidak mencetak apa pun di sh. Ini karena @ di sh melempar kesalahan yang disalurkan ke /dev/null dan setelah itu komentar dimulai. Pada cmd pipa ke /dev/null gagal karena file tidak dikenali di windows tetapi karena windows tidak terdeteksi # sebagai komentar kesalahan disalurkan ke nul. Kemudian itu bergema. Karena seluruh baris didahului oleh suatu @ tidak mendapatkan printet pada cmd.
  • Yang kedua mendefinisikan ::, yang memulai komentar di cmd, ke noop di sh. Ini memiliki manfaat itu :: tidak diatur ulang $? untuk 0. Ia menggunakan ":; adalah label "trik.
  • Sekarang saya bisa menambahkan perintah sh dengan :: dan mereka diabaikan dalam cmd
  • Di :: exit skrip sh berakhir dan saya dapat menulis perintah cmd
  • Hanya baris pertama (shebang) yang bermasalah dalam cmd karena akan dicetak command not found. Anda harus memutuskan sendiri apakah Anda membutuhkannya atau tidak.

0
2018-02-23 08:00



Ada platform yang membangun alat independen seperti Ant atau Maven dengan sintaks xml (berdasarkan Java). Jadi, Anda dapat menulis ulang semua skrip Anda di Semut atau Maven untuk menjalankannya meskipun jenis os. Atau Anda bisa membuat skrip pembungkus Ant, yang akan menganalisis jenis os dan menjalankan skrip kelelawar atau bash yang sesuai.


-5
2017-07-07 09:10