Câu hỏi Kịch bản duy nhất để chạy trong cả Windows và Linux Bash?


Có thể viết một tệp kịch bản lệnh thực hiện trong cả Windows (được xử lý dưới dạng .bat) và Linux (qua Bash) không?

Tôi biết cú pháp cơ bản của cả hai, nhưng không tìm ra. Nó có lẽ có thể khai thác một số cú pháp tối nghĩa của Bash hoặc một số trục trặc xử lý hàng loạt Windows.

Lệnh thực hiện có thể chỉ là một dòng để thực thi tập lệnh khác.

Động lực là chỉ có một lệnh khởi động ứng dụng duy nhất cho cả Windows và Linux.

Cập nhật: Sự cần thiết cho kịch bản shell "bản địa" của hệ thống là nó cần phải chọn phiên bản phiên dịch đúng, phù hợp với các biến môi trường nổi tiếng nhất định vv Cài đặt các môi trường bổ sung như CygWin không thích hợp hơn - Tôi muốn giữ khái niệm "download & chạy".

Ngôn ngữ khác duy nhất để xem xét cho Windows là Windows Scripting Host - WSH, được cài sẵn theo mặc định từ 98.


76
2017-07-07 09:05


gốc


Nhìn vào Perl hoặc Python. Đó là các ngôn ngữ kịch bản có sẵn trên cả hai nền tảng. - a_horse_with_no_name
groovy, python, ruby ​​bất cứ ai? stackoverflow.com/questions/257730/… - Kalpesh Soni
Groovy sẽ là tuyệt vời để có trên tất cả các hệ thống theo mặc định, tôi sẽ lấy nó như là một shell scripting lang. Có thể một ngày nào đó :) - Ondra Žižka
Xem thêm: github.com/BYVoid/Batsh - Dave Jarvis
Một trường hợp sử dụng hấp dẫn cho việc này là hướng dẫn - để có thể chỉ cho mọi người biết "chạy tập lệnh nhỏ này" mà không cần phải giải thích rằng "nếu bạn đang sử dụng Windows, hãy sử dụng tập lệnh nhỏ này, nhưng nếu bạn sử dụng Linux hoặc Mac, hãy thử thay vào đó, tôi chưa thực sự thử nghiệm vì tôi đang sử dụng Windows. " Đáng buồn là Windows không có các lệnh cơ bản như Unix cp được xây dựng, hoặc ngược lại để viết hai kịch bản riêng biệt có thể tốt hơn về mặt sư phạm so với các kỹ thuật nâng cao được hiển thị ở đây. - Qwertie


Các câu trả lời:


Những gì tôi đã làm là sử dụng cú pháp nhãn của cmd làm dấu nhận xét. Ký tự nhãn, dấu hai chấm (:), tương đương với true trong hầu hết các vỏ POSIXish. Nếu bạn ngay lập tức theo dõi ký tự nhãn bằng một ký tự khác không thể sử dụng trong GOTO, sau đó nhận xét cmd tập lệnh sẽ không ảnh hưởng đến cmd mã.

Việc hack là đặt các dòng mã sau chuỗi ký tự “:;”. Nếu bạn đang viết hầu hết các tập lệnh một đoạn hoặc có thể viết một dòng sh cho nhiều dòng cmd, sau đây có thể là tốt. Đừng quên rằng bất kỳ việc sử dụng $? phải ở trước dấu hai chấm tiếp theo của bạn : bởi vì : đặt lại $? đến 0.

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

Một ví dụ rất có tính cách bảo vệ $?:

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

Một ý tưởng khác để bỏ qua cmd mã là sử dụng heredocs để vậy sh đối xử với cmd mã như một chuỗi không sử dụng và cmd diễn giải nó. Trong trường hợp này, chúng tôi đảm bảo rằng dấu phân cách của heredoc của chúng tôi đều được trích dẫn (để dừng sh từ việc giải thích về nội dung của nó khi chạy với sh) và bắt đầu với : để vậy cmd bỏ qua nó giống như bất kỳ dòng nào khác bắt đầu bằng :.

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

Tùy thuộc vào nhu cầu của bạn hoặc phong cách mã hóa, xen kẽ cmd và sh mã có thể hoặc có thể không có ý nghĩa. Sử dụng heredocs là một phương pháp để thực hiện xen kẽ như vậy. Điều này có thể, tuy nhiên, được mở rộng với GOTO kỹ thuật:

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

Tất nhiên, các bình luận chung có thể được thực hiện với chuỗi ký tự : # hoặc là :;#. Không gian hoặc dấu chấm phẩy là cần thiết vì sh xem xét # là một phần của tên lệnh nếu nó không phải là ký tự đầu tiên của số nhận dạng. Ví dụ: bạn có thể muốn viết nhận xét chung trong các dòng đầu tiên của tệp trước khi sử dụng GOTO để tách mã của bạn. Sau đó, bạn có thể thông báo cho người đọc của bạn về lý do tại sao kịch bản của bạn được viết rất kỳ quặc:

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

Do đó, một số ý tưởng và cách thức để thực hiện sh và cmdkịch bản tương thích không có tác dụng phụ nghiêm trọng theo như tôi biết (và không có cmdđầu ra '#' is not recognized as an internal or external command, operable program or batch file.).


65
2017-07-12 20:44



Chú ý điều này đòi hỏi kịch bản phải có .cmd mở rộng, nếu không nó sẽ không chạy trong Windows. Có bất kỳ công việc xung quanh? - jviotti
Nếu bạn đang viết các tệp hàng loạt, tôi khuyên bạn chỉ nên đặt tên cho chúng .cmd và đánh dấu chúng là có thể thực thi được. Bằng cách đó, thực thi kịch bản từ trình bao mặc định trên Windows hoặc Unix sẽ hoạt động (chỉ được thử nghiệm với bash). Bởi vì tôi không thể nghĩ ra một cách để bao gồm một shebang mà không làm cmd phàn nàn lớn tiếng, bạn phải luôn luôn gọi kịch bản thông qua trình bao. Tức là, hãy chắc chắn sử dụng execlp() hoặc là execvp() (Tôi nghĩ system() làm điều này "đúng" cách cho bạn) hơn là execl() hoặc là execv() (các chức năng này sẽ chỉ hoạt động trên các kịch bản với các shebang vì chúng trực tiếp đi vào hệ điều hành). - binki
Tôi đã bỏ qua thảo luận về các phần mở rộng tệp vì tôi đã viết mã di động của mình trong các trình vỏ (ví dụ: <Exec/> MSBuild Task) chứ không phải là tệp hàng loạt độc lập. Có lẽ nếu tôi có thời gian trong tương lai, tôi sẽ cập nhật câu trả lời với thông tin trong các nhận xét này… - binki


bạn có thể thử điều này:

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

Có lẽ bạn sẽ cần phải sử dụng /r/n dưới dạng một dòng mới thay vì kiểu unix. Nếu tôi nhớ chính xác dòng unix mới không được hiểu là dòng mới bằng .bat scripts.Một cách khác là tạo một #.exe tập tin trong đường dẫn mà không làm gì theo cách tương tự như câu trả lời của tôi ở đây: Có thể nhúng và thực thi VBScript trong một tập tin thực thi mà không cần sử dụng một tệp tạm thời không?

CHỈNH SỬA

Các câu trả lời của binki gần như hoàn hảo nhưng vẫn có thể cải thiện:

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

echo $PATH

Nó sử dụng một lần nữa : lừa và dòng comment.Looks như cmd.exe (ít nhất là trên windows10) hoạt động mà không có vấn đề với các EOL phong cách unix vì vậy hãy chắc chắn rằng kịch bản của bạn được chuyển đổi thành định dạng linux. (cách tiếp cận tương tự đã được sử dụng trước đây đây và đây ). Mặc dù sử dụng shebang vẫn sẽ tạo ra sản lượng dư thừa ...


16
2017-07-07 09:29



Có vẻ tốt, sẽ cố gắng. - Ondra Žižka
/r/n ở cuối dòng sẽ gây ra nhiều cơn đau đầu trong tập lệnh bash trừ khi bạn kết thúc mỗi dòng với # (I E. #/r/n) - theo cách đó \r sẽ được coi là một phần của nhận xét và bị bỏ qua. - Gordon Davisson
Trên thực tế, tôi thấy rằng # 2>NUL & ECHO Welcome to %COMSPEC%. quản lý để ẩn lỗi về # lệnh không được tìm thấy bởi cmd. Kỹ thuật này rất hữu ích khi bạn cần viết một dòng độc lập sẽ chỉ được thực thi bởi cmd (ví dụ: trong Makefile). - binki


Tôi muốn bình luận, nhưng chỉ có thể thêm câu trả lời vào lúc này.

Các kỹ thuật được đưa ra là tuyệt vời và tôi cũng sử dụng chúng.

Thật khó để giữ lại một tệp có hai loại ngắt dòng chứa trong đó, đó là /n cho phần bash và /r/n cho phần cửa sổ. Hầu hết các biên tập viên đều cố gắng và thực thi một chương trình ngắt dòng thông thường bằng cách đoán loại tệp bạn đang chỉnh sửa. Ngoài ra, hầu hết các phương thức chuyển tệp qua internet (đặc biệt là tệp văn bản hoặc tập lệnh) sẽ giải phóng các ngắt dòng, vì vậy bạn có thể bắt đầu bằng một loại ngắt dòng và kết thúc bằng cách khác. Nếu bạn đưa ra giả định về ngắt dòng và sau đó đã đưa tập lệnh của bạn cho người khác để sử dụng, họ có thể thấy nó không hoạt động đối với họ.

Vấn đề khác là hệ thống tập tin gắn trên mạng (hoặc CD) được chia sẻ giữa các loại hệ thống khác nhau (đặc biệt là nơi bạn không thể kiểm soát phần mềm có sẵn cho người dùng).

Do đó, bạn nên sử dụng ngắt dòng DOS /r/n và cũng bảo vệ tập lệnh bash từ DOS /r bằng cách đặt nhận xét ở cuối mỗi dòng (#). Bạn cũng không thể sử dụng các dòng liên tục trong bash vì /r sẽ khiến họ phá vỡ.

Bằng cách này, bất cứ ai sử dụng kịch bản, và trong bất cứ môi trường nào, nó sẽ hoạt động.

Tôi sử dụng phương pháp này kết hợp với tạo Makefiles di động!


10
2017-12-17 13:58





Các công việc sau đây cho tôi mà không có bất kỳ lỗi hoặc thông báo lỗi nào với Bash 4 và Windows 10, không giống như các câu trả lời ở trên. Tôi đặt tên tệp là "whatever.cmd", chmod +x để làm cho nó thực thi được trong linux, và làm cho nó có kết thúc dòng unix (dos2unix) để giữ cho bash yên tĩnh.

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





Có một số cách để thực hiện các lệnh khác nhau trên bash và cmd với cùng một tập lệnh.

cmd sẽ bỏ qua các dòng bắt đầu bằng :;, như đã đề cập trong các câu trả lời khác. Nó cũng sẽ bỏ qua dòng tiếp theo nếu dòng hiện tại kết thúc bằng lệnh rem ^, như ^ ký tự sẽ thoát khỏi ngắt dòng và dòng tiếp theo sẽ được coi là nhận xét bởi rem.

Để làm bash bỏ qua cmd dòng, có nhiều cách. Tôi đã liệt kê một số cách để làm điều đó mà không vi phạm cmdlệnh:

Không tồn tại # lệnh (không được khuyến nghị)

Nếu không có # lệnh có sẵn trên cmd khi tập lệnh được chạy, chúng tôi có thể thực hiện điều này:

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

Các # nhân vật ở đầu của cmd dòng làm cho bash coi dòng đó là nhận xét.

Các # nhân vật ở cuối bash dòng được sử dụng để nhận xét \r nhân vật, như Brian Tompsett đã chỉ ra câu trả lời của anh ấy. Không có điều này, bash sẽ ném một lỗi nếu tệp có \r\n kết thúc dòng, được yêu cầu bởi cmd.

Bằng cách làm # 2>nul, chúng tôi đang lừa cmd bỏ qua lỗi của một số người không tồn tại # lệnh, trong khi vẫn thực hiện lệnh sau.

Không sử dụng giải pháp này nếu có # lệnh có sẵn trên PATH hoặc nếu bạn không kiểm soát được các lệnh có sẵn cmd.


Sử dụng echo bỏ qua # ký tự trên cmd

Chúng ta có thể sử dụng echo với đầu ra của nó được chuyển hướng để chèn cmd lệnh trên bashKhu vực đã nhận xét:

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

Kể từ khi # nhân vật không có ý nghĩa đặc biệt cmd, nó được coi là một phần của văn bản echo. Tất cả những gì chúng tôi phải làm là chuyển hướng đầu ra của echo lệnh và chèn các lệnh khác sau nó.


Trống #.bat tập tin

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

Các echo >/dev/null # 1>nul 2> #.bat dòng tạo ra một sản phẩm nào #.bat tập tin trong khi trên cmd (hoặc thay thế hiện tại #.bat, nếu có), và không làm gì trong khi trên bash.

Tệp này sẽ được sử dụng bởi cmd (các) dòng sau ngay cả khi có một số khác # lệnh trên PATH.

Các del #.bat lệnh trên cmd-specific mã xóa các tập tin đã được tạo ra. Bạn chỉ phải làm điều này vào lần cuối cmd hàng.

Không sử dụng giải pháp này nếu #.bat tệp có thể nằm trong thư mục làm việc hiện tại của bạn, vì tệp đó sẽ bị xóa.


Đã đề xuất: sử dụng tài liệu ở đây bỏ qua cmd lệnh trên bash

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

Bằng cách đặt ^ nhân vật ở cuối cmd dòng chúng tôi đang thoát khỏi ngắt dòng và bằng cách sử dụng : như dấu phân tách tài liệu ở đây, nội dung của đường phân cách sẽ không có hiệu lực cmd. Bằng cách đó, cmd sẽ chỉ thực hiện dòng của nó sau : dòng kết thúc, có hành vi tương tự như bash.

Nếu bạn muốn có nhiều dòng trên cả hai nền tảng và chỉ thực hiện chúng ở cuối khối, bạn có thể thực hiện việc này:

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

Miễn là không có cmd phù hợp với chính xác here-document delimiter, giải pháp này sẽ hoạt động. Bạn có thể thay đổi here-document delimiter cho bất kỳ văn bản nào khác.


Trong tất cả các giải pháp được trình bày, các lệnh sẽ chỉ được thực thi sau dòng cuối cùng, làm cho hành vi của họ nhất quán nếu họ làm điều tương tự trên cả hai nền tảng.

Các giải pháp đó phải được lưu vào các tệp có \r\n làm ngắt dòng, nếu không chúng sẽ không hoạt động cmd.


2
2017-07-27 03:18



Tốt hơn là không bao giờ ... điều này đã giúp tôi nhiều hơn những câu trả lời khác. Cảm ơn!! - A-Diddy


Các câu trả lời trước đây dường như bao gồm khá nhiều tùy chọn và giúp tôi rất nhiều. Tôi bao gồm câu trả lời ở đây chỉ để chứng minh cơ chế mà tôi đã sử dụng để bao gồm cả tập lệnh Bash và tập lệnh Windows CMD trong cùng một tệp.

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.

Tóm lược

Trong Linux

Dòng đầu tiên (echo >/dev/null # >nul & GOTO WINDOWS & rem ^) sẽ bị bỏ qua và tập lệnh sẽ chạy qua từng dòng ngay lập tức sau khi nó được hiển thị cho đến khi exit 0 lệnh được thực hiện. Một lần exit 0 đạt được, việc thực thi kịch bản sẽ kết thúc, bỏ qua các lệnh Windows bên dưới nó.

Trong Windows

Dòng đầu tiên sẽ thực thi GOTO WINDOWS lệnh, bỏ qua các lệnh Linux ngay sau nó và tiếp tục thực hiện tại :WINDOWS hàng.

Loại bỏ trả về vận chuyển trong Windows

Vì tôi đã chỉnh sửa tệp này trong Windows, tôi đã phải loại bỏ hệ thống trả về vận chuyển (\ r) từ các lệnh Linux hoặc nếu không có kết quả bất thường khi chạy phần Bash. Để làm điều này, tôi đã mở tệp trong Notepad ++ và làm như sau:

  1. Bật tùy chọn để xem các ký tự cuối dòng (View> Show Symbol > Show End of Line). Lợi nhuận vận chuyển sau đó sẽ hiển thị dưới dạng CR nhân vật.

  2. Thực hiện Tìm kiếm & Thay thế (Search > Replace...) và kiểm tra Extended (\n, \r, \t, \0, \x...) Tùy chọn.

  3. Kiểu \r bên trong Find what : và bỏ trống Replace with : để không có gì trong đó.

  4. Bắt đầu ở đầu tệp, nhấp vào Replace nút cho đến khi tất cả các vận chuyển trở lại (CR) các ký tự đã bị xóa khỏi phần Linux hàng đầu. Hãy chắc chắn để lại vận chuyển trở lại (CR) ký tự cho phần Windows.

Kết quả phải là mỗi lệnh Linux kết thúc bằng một dòng cấp dữ liệu (LF) và mỗi lệnh Windows kết thúc bằng một dòng trả về và dòng thức ăn (CR  LF).


1
2018-02-22 22:46





Bạn có thể chia sẻ các biến:

:;SET() { eval $1; }

SET var=value

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

0
2017-10-04 12:44





Tôi sử dụng kỹ thuật này để tạo các tệp jar có thể chạy được. Vì tệp jar / zip bắt đầu ở tiêu đề zip, tôi có thể đặt một tập lệnh phổ quát để chạy tệp này ở trên cùng:

#!/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
  • Dòng đầu tiên không lặp lại trong cmd và không in bất cứ thứ gì trên sh. Điều này là do @ trong sh ném một lỗi được dẫn đến /dev/null và sau đó một bình luận bắt đầu. Trên cmd đường ống đến /dev/null không thành công vì tệp không được nhận dạng trên cửa sổ nhưng do cửa sổ không phát hiện được # như một bình luận lỗi được dẫn đến nul. Sau đó, nó làm một tiếng vang tắt. Bởi vì toàn bộ dòng được bắt đầu bởi một @ nó không nhận được printet trên cmd.
  • Cái thứ hai định nghĩa ::, bắt đầu một bình luận trong cmd, để noop trong sh. Điều này có lợi ích :: không đặt lại $? đến 0. Nó sử dụng ":; là một nhãn "lừa.
  • Bây giờ tôi có thể thêm lệnh sh vào :: và chúng bị bỏ qua trong cmd
  • Trên :: exit kịch bản sh kết thúc và tôi có thể viết lệnh cmd
  • Chỉ dòng đầu tiên (shebang) là có vấn đề trong cmd vì nó sẽ in command not found. Bạn phải tự quyết định xem mình có cần hay không.

0
2018-02-23 08:00





Có một nền tảng độc lập xây dựng các công cụ như Ant hoặc Maven với cú pháp xml (dựa trên Java). Vì vậy, bạn có thể viết lại tất cả các kịch bản của bạn trong Ant hoặc Maven chạy chúng mặc dù loại os. Hoặc bạn chỉ có thể tạo kịch bản trình bao bọc Ant, sẽ phân tích loại os và chạy tập lệnh bash hoặc bash thích hợp.


-5
2017-07-07 09:10



Điều đó có vẻ như một sự lạm dụng tổng thể của những công cụ đó. Nếu bạn muốn tránh câu hỏi thực tế (chạy trong shell nguyên gốc) thì bạn nên đề xuất một ngôn ngữ kịch bản phổ quát, như python, không phải công cụ xây dựng có khả năng bị lạm dụng như một sự thay thế cho một ngôn ngữ kịch bản thích hợp. - ArtOfWarfare