Tệp tập lệnh thực thi chạy trên POSIX và Windows


16

Thách thức : viết một tệp tập lệnh duy nhất foo.cmdcó thể được gọi từ cmd.exedấu nhắc Windows vanilla (không phải PowerShell, không ở chế độ quản trị viên), để thực thi mã cụ thể của Windows ...

> .\foo.cmd
Hello Windows! 

... mà còn được gọi không thay đổi gì từ một điển hình POSIX-compliant (Linux / OSX) shell prompt ( bash, tcshhay zsh), để thực hiện tùy tiện đang POSIX cụ thể:

$ chmod a+x foo.cmd
$ ./foo.cmd
Hello POSIX!

... mà không yêu cầu cài đặt hoặc tạo trình thông dịch / công cụ của bên thứ ba.

Tôi biết điều này là có thể, nhưng với cruft (tức là trên Windows, một hoặc hai dòng rác / thông báo lỗi được in thành thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn trước "Xin chào Windows!").

Tiêu chí chiến thắng là tối thiểu hóa (thứ nhất) số lượng dòng cruft và (thứ hai) số lượng ký tự cruft.

Cruft có thể được định nghĩa là bất kỳ đầu ra giao diện điều khiển (stdout hoặc stderr) không được sản xuất bởi mã tải trọng (tùy ý). Các dòng trống được tính trong số dòng. Dòng mới không được tính vào số lượng nhân vật. Điểm số Cruft nên được tổng hợp trên cả hai nền tảng. Chúng ta hãy bỏ qua các cơ chế như thế clsđể quét sạch hành trình nhưng với chi phí cũng làm trống đầu ra thiết bị đầu cuối trước đó. Nếu Windows lặp lại các lệnh của bạn vì bạn chưa bật @echo off, hãy loại trừ các ký tự mà nó dành để in thư mục hiện tại và lời nhắc.

Một tiêu chí phụ là sự đơn giản / thanh lịch của giải pháp bên trong foo.cmd: nếu "cơ sở hạ tầng" được định nghĩa là bất kỳ ký tự nào không liên quan trực tiếp đến mã tải trọng tùy ý, thì trước tiên giảm thiểu số lượng dòng chứa ký tự cơ sở hạ tầng và thứ hai tổng số cơ sở hạ tầng nhân vật.

Thêm kudos nếu phần POSIX sẽ hoạt động mặc dù tệp có kết thúc dòng CRLF! (Tôi không chắc rằng phần cuối thậm chí có thể.)

Giải pháp hiện tại của tôi, mà tôi sẽ đăng ở đây một khi những người khác có cơ hội, sử dụng 6 dòng mã cơ sở hạ tầng (52 ký tự không bao gồm dòng mới). Nó tạo ra 5 dòng cruft, hai trong số đó là trống, tất cả đều xảy ra trên Windows (30 ký tự không bao gồm dòng mới và loại trừ chuỗi thư mục / dấu nhắc hiện tại xuất hiện trên hai trong số các dòng đó).


được một kịch bản shell / mẻ, ya?
con mèo

1
Có ai biết về một môi trường dùng thử trực tuyến cho DOS không?
Chấn thương kỹ thuật số

1
Tôi đã tìm thấy cái này nhưng nó không cho phép tôi nhập các ký tự ":", "\", "{" hoặc "}": - /
Chấn thương kỹ thuật số

@DigitalTrauma Có Rượu vang (mà bạn nên cài đặt, nếu không, vì nó tiện dụng)
mèo

1
@cat cảm ơn - Câu trả lời của tôi dường như làm việc dưới rượu bây giờ.
Chấn thương kỹ thuật số

Câu trả lời:


15

0 dòng cruft, 0 ký tự cruft, 2 infra. dòng, 21 infra. ký tự, CRLF ok

:<<@goto:eof
@echo Hello Windows!
@goto:eof
echo "Hello POSIX!" #

Loại bỏ các giải pháp khác.

17 ký tự sử dụng exit /btừ câu trả lời của Digital Trauma:

:<<@exit/b
@echo Hello Windows!
@exit/b
echo "Hello POSIX!" #

Ứng cử viên mạnh mẽ! Đặc biệt là phiên bản thứ hai (về tiêu chí đơn giản / thanh lịch, sẽ tốt hơn nhiều khi không phải di chuyển các dòng tải trọng theo cách mà phiên bản đầu tiên làm). Điều này chạy cho tôi trên Windows và OSX. Ban đầu, tôi nhận được một dòng nói rằng : command not foundbất cứ khi nào một dòng trống len lỏi vào trọng tải posix, nhưng cuối cùng tôi đã nhận ra rằng đó không phải là về :dòng đầu tiên mà là do CRLF không được bảo vệ #. Đó là tin tức với tôi rằng một #!dòng không cần thiết mà chịu trách nhiệm cho hai trong số các dòng Windows cruft trong phiên bản trước của tôi.
Jez

Phiên bản đầu tiên rất khó để ghi điểm vì nó có thể thêm các ký tự : ` >&3` \ vào mỗi dòng tải, tôi đoán bạn có thể nói chi phí cơ sở hạ tầng của nó cao tùy ý.
Jez

@jez Bạn đang tính điểm số cho câu trả lời? Nếu vậy bạn cần phải làm cho nó rõ ràng hơn nhiều trong câu hỏi làm thế nào để làm điều đó.
Chấn thương kỹ thuật số

Tôi chưa quen với codegolf, vì vậy TBH tôi nghĩ rằng tôi dự kiến ​​sẽ ghi được câu trả lời một cách khách quan bằng cách nào đó. Tuy nhiên, tôi nghĩ rằng câu hỏi làm cho nó rõ ràng: đầu tiên, số lượng đường bay; phá vỡ mối quan hệ trên đó bằng số lượng ký tự; sau đó về số lượng cơ sở hạ tầng, sau đó số lượng ký tự cơ sở hạ tầng. Tôi đã không mong đợi các giải pháp vượt qua các dòng tải trọng, như giải pháp đầu tiên của jimmy. Tôi sẽ thay đổi câu hỏi một chút để làm cho nó rõ ràng là điều không mong muốn (xin lỗi vì sự thay đổi mục tiêu nhỏ đó nhưng tôi không nghĩ rằng nó ảnh hưởng đến giải pháp thứ hai của anh ấy hoặc bất kỳ phiên bản nào của bạn cho đến nay)
jez

Có vẻ như câu trả lời của chúng tôi đang hội tụ ;-) Bạn có lợi thế trong việc ghi điểm mặc dù với việc sử dụng heredoc. Là cuối cùng #cần thiết?
Chấn thương kỹ thuật số

4

Điểm 0 cruft + 4 infra lines + 32 infra chars. LF & CRLF OK.

Điều này dựa trên những gì tôi tìm thấy ở blog này , với các bit Amiga và các dòng không cần thiết khác được lấy ra. Tôi đã ẩn các dòng DOS trong các trích dẫn được nhận xét thay vì \tiếp tục sử dụng dòng này để nó có thể hoạt động với cả CRLF và LF.

@REM ()(:) #
@REM "
@ECHO Hello Windows!
@EXIT /B
@REM "
echo Hello POSIX!

Với các kết thúc dòng DOS CRLF hoặc * nix LF, nó hoạt động trên Ubuntu, OSX và rượu vang:

ubuntu@ubuntu:~$ ./dosix.bat
Hello POSIX!
ubuntu@ubuntu:~$ wine cmd.exe
Wine CMD Version 5.1.2600 (1.6.2)

Z:\home\ubuntu>dosix.bat
Hello Windows!

Z:\home\ubuntu>exit
ubuntu@ubuntu:~$ 

Để tạo chính xác điều này (với CRLF) trên máy * nix (bao gồm cả OSX), hãy dán đoạn sau vào thiết bị đầu cuối:

[ $(uname) = Darwin ] && decode=-D || decode=-d
ubuntu@ubuntu:~$ base64 $decode > dosix.bat << EOF
QFJFTSAoKSg6KSAjDQpAUkVNICINCkBFQ0hPIEhlbGxvIFdpbmRvd3MhDQpARVhJVCAvQg0KQFJF
TSAiDQplY2hvIEhlbGxvIFBPU0lYIQ0K
EOF

Có vẻ tốt đẹp! dosixlà một cái tên đẹp quá Nhưng trên máy Mac của tôi (OS 10.9.4, Darwin Kernel Phiên bản 13.3.0, GNU bash phiên bản 3.2.51) thì nó không chạy được với: Bạn có ./dosix.cmd: line 13: syntax error: unexpected end of file biết tại sao không?
Jez

@jez đảm bảo bạn lưu tệp được mã hóa bằng UTF-8 và giữ nguyên các kết thúc dòng Windows!
mèo

Ah, nó đã xảy ra bởi vì tôi vô tình có dòng kết thúc Windows và đang sử dụng phiên bản đầu tiên.
Jez

Lưu ý rằng bạn có thể nhập ký tự CR trong Bash bằng cách chèn (hoặc Cv), nhập (hoặc Cm).
jimmy23013

@jez Vì vậy, điểm này là 0 dòng cruft + 4 dòng cơ sở hạ tầng + 32 ký tự cơ sở hạ tầng. Những con số này có nên được kết hợp theo bất kỳ cách có ý nghĩa nào để tạo ra một điểm số duy nhất để so sánh không?
Chấn thương kỹ thuật số

2

Tôi sẽ đăng giải pháp tôi đã sử dụng, vì nó đã bị đánh bại. Đó là phép lịch sự của một đồng nghiệp của tôi, người mà tôi nghĩ phải đọc cùng một mục blog như Digital Trauma .

#!/bin/sh # >NUL 2>&1
echo \
@goto c \
>/dev/null
echo "Hello Posix!"
exit
:c
@echo Hello Windows!
  • Windows cruft: 5 dòng (trong đó hai dòng trống) / 30 ký tự
  • Hành trình OSX: 0
  • Cơ cấu hạ tầng: 6 dòng / 52 ký tự
  • Khả năng tương thích CRLF: chỉ khi trình thông dịch có tên trên #!dòng không quan tâm (vì vậy đối với người phiên dịch chuẩn như shbạn bè, thì không thành công)

Trên POSIX, tập lệnh luôn bắt đầu bằng #!, có nghĩa là tập lệnh của bạn là câu trả lời duy nhất cho đến nay là tập lệnh hợp lệ trên hệ thống POSIX. Chắc chắn một số người khác có thể chạy nếu - nhưng chỉ khi chúng được bắt đầu từ trình bao với cách giải quyết cho các tập lệnh bị lỗi.
kasperd

Tốt để biết! Tôi đoán tôi mờ về các tiêu chí nghiêm ngặt để tuân thủ POSIX. Mục tiêu thực sự của tôi là có các tệp script hoạt động "vượt trội" trên "hầu hết" các hệ thống máy tính để bàn hiện đại, bất kể điều đó có nghĩa là gì - Windows 7/8/10, Ubuntu, OSX ... Cách giải quyết phổ biến cho các tập lệnh shebangless, Tôi tự hỏi? Dù sao, tôi sẽ không tự thưởng điểm cho mình :-)
jez

Tôi đã không nhận thấy rằng cả câu trả lời và câu hỏi được viết bởi cùng một người. Tôi nghĩ rằng tất cả các shell mà tôi đã làm việc có một cách giải quyết cho một #!dòng bị thiếu , nhưng chúng sẽ sử dụng các shell khác nhau để diễn giải kịch bản. Điều đó có nghĩa là một "tập lệnh" không bắt đầu #!phải có giá trị trong không chỉ một vỏ, mà mọi vỏ mà nó có thể được giải thích hợp lý. Nhưng thậm chí tệ hơn, nó chỉ hoạt động khi được bắt đầu từ một vỏ khác. Một kịch bản như vậy có thể hoạt động từ dòng lệnh, nhưng không phải trong bối cảnh cuối cùng bạn có ý định sử dụng nó.
kasperd

Cảm ơn bạn, đây là công cụ rất giáo dục và chính xác là thứ mà tôi đã hy vọng học được bằng cách bắt đầu thử thách này. Đối với các trường hợp điều này sẽ (hoặc có thể) có vấn đề, #!dòng trong câu trả lời đã được chỉnh sửa của tôi (1 dòng cơ sở hạ tầng với 21 ký tự) có thể được kết hợp với câu trả lời của bất kỳ ai khác với chi phí hành trình chỉ dành cho Windows là 2 dòng (trong đó một dòng trống) hoặc 23 ký tự.
jez

2

Tóm tắt / tổng hợp các câu trả lời và thảo luận

Điều này thật thú vị, và tôi đã học được rất nhiều.

Một số hành trình cụ thể của Windows là không thể tránh khỏi nếu trên hệ thống POSIX của bạn, bạn cần bắt đầu tập lệnh của mình bằng một #!dòng. Nếu bạn không có cách nào khác ngoài việc này, thì dòng này:

#!/bin/sh # 2>NUL

có lẽ là tốt nhất nó có thể nhận được. Nó làm cho một dòng trống và một dòng cruft được xuất ra trên bảng điều khiển Windows. Tuy nhiên, bạn thể thoát khỏi mà không cần một #!dòng: trên hầu hết các hệ thống, một trong những trình thông dịch shell thông thường sẽ kết thúc việc thực thi tập lệnh (vấn đề là nó không thể dự đoán được về trình thông dịch nào - nó sẽ phụ thuộc vào, nhưng sẽ không nhất thiết phải giống hệt với trình bao bạn sử dụng để gọi lệnh).

Ngoài dòng đầu tiên khó khăn đó, có một số giải pháp không cần thiết thực sự khéo léo. Đệ trình chiến thắng của jimmy23013 chỉ bao gồm hai dòng cơ sở hạ tầng ngắn và sử dụng vai trò kép của :nhân vật để thực hiện một dòng "im lặng" trên cả hai nền tảng (như một người không thực tếsh và bạn bè, và như một nhãn hiệu đánh dấu trong cmd.exe):

:<<@exit/b

:: Arbitrary Windows code goes here

@exit/b
#
# Arbitrary POSIX code goes here 

Đó khả năng để làm như vậy một kịch bản chạy trên các hệ thống POSIX thậm chí bất chấp CRLF dòng kết thúc, nhưng để làm được điều này đối với hầu hết các thông dịch viên, bạn phải chấm dứt tất cả các dòng của phần POSIX của bạn (ngay cả dòng trống) với một bình luận hoặc nhận xét nhân vật.

Cuối cùng, đây là hai biến thể của một giải pháp tôi đã phát triển dựa trên đầu vào của mọi người. Chúng có thể gần như là tốt nhất trong tất cả các thế giới, trong đó chúng giảm thiểu thiệt hại từ việc thiếu #!và làm cho khả năng tương thích CRLF thậm chí còn mượt mà hơn. Hai dòng cơ sở hạ tầng bổ sung là cần thiết. Chỉ có một dòng (được chuẩn hóa) phải được giải thích bởi trình bao POSIX không thể đoán trước và dòng đó cho phép bạn chọn trình bao cho phần còn lại của tập lệnh ( bashtrong ví dụ sau):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
bash "$@"<<:Z
#
echo "Hello POSIX!" #
ps # let's throw this into the payload to keep track of which shell is being used
#
:Z

Một phần của vẻ đẹp của các giải pháp di truyền này là chúng vẫn còn mạnh mẽ CRLF: miễn là <<:Zcuối dòng, bộ xử lý heredoc sẽ thực sự tìm kiếm và sẽ tìm thấy mã thông báo:Z\r

Như một bước ngoặt cuối cùng, bạn có thể loại bỏ những bình luận cuối dòng phiền phức đó mà vẫn giữ được sự mạnh mẽ của CRLF, bằng cách tước các \rký tự ra trước khi chuyển các dòng vào vỏ. Điều này đặt niềm tin nhiều hơn vào lớp vỏ không thể đoán trước (sẽ tốt hơn khi sử dụng { tr -d \\r|bash;}thay vì (tr -d \\r|bash)nhưng dấu ngoặc nhọn là cú pháp chỉ bash):

:<<@GOTO:Z

@echo Hello Windows!

@GOTO:Z
(tr -d \\r|bash "$@")<<:Z

echo "Hello POSIX!"
ps

:Z

Tất nhiên, cách tiếp cận này hy sinh khả năng chuyển đầu vào stdin vào tập lệnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.