it-swarm.asia

برنامج نصي واحد لتشغيله في كل من Windows و Linux Bash؟

هل من الممكن كتابة ملف نصي واحد ينفذ في كل من Windows (يُعامل كـ .bat) و Linux (عبر Bash)؟

أعرف بناء الجملة الأساسي لكليهما ، لكن لم أعرف. من المحتمل أن تستغل بناء جملة غامض من بعض Bash أو بعض خلل معالج دفعي Windows.

قد يكون أمر التنفيذ مجرد سطر واحد لتنفيذ برنامج نصي آخر.

الدافع هو الحصول على أمر تمهيد تطبيق واحد لكل من Windows و Linux.

التحديث: الحاجة إلى برنامج شل "الأصلي" للنظام هو أنه يحتاج إلى اختيار إصدار المترجم الصحيح ، ويتوافق مع بعض متغيرات البيئة المعروفة ، إلخ. إن تثبيت بيئات إضافية مثل CygWin أمر غير مفضل - أود أن الحفاظ على مفهوم "تحميل وتشغيل".

اللغة الأخرى الوحيدة التي يجب مراعاتها في Windows هي Windows Scripting Host - WSH ، والتي تم إعدادها مسبقًا افتراضيًا منذ 98.

85
Ondra Žižka

ما قمت به هو استخدام بناء جملة تسمية cmd كعلامة تعليق . حرف التسمية ، نقطتان (:) ، تعادل true في معظم قذائف POSIXish. إذا كنت تتابع على الفور حرف التسمية بواسطة حرف آخر لا يمكن استخدامه في GOTO ، فلن يؤثر تعليق البرنامج النصي cmd على رمز cmd الخاص بك.

الاختراق هو وضع أسطر من التعليمات البرمجية بعد تسلسل الأحرف ":;". إذا كنت تكتب في الغالب نصوصًا برمجية واحدة أو ، كما هو الحال ، يمكنك كتابة سطر واحد من sh للعديد من أسطر cmd ، قد يكون التالي على ما يرام. لا تنسَ أن أي استخدام لـ $? يجب أن يكون قبل النقطتين التاليتين : لأن : يعيد تعيين $? إلى 0.

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

مثال مفتعل جدًا للحراسة $?:

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

هناك فكرة أخرى لتخطي رمز cmd وهي استخدام heredocs بحيث يعامل sh رمز cmd كسلسلة غير مستخدمة و cmd يفسرها. في هذه الحالة ، نحرص على أن يكون محدد حدود heredoc مقتبسًا (لإيقاف sh من القيام بأي نوع من التفسير على محتوياته عند التشغيل باستخدام sh) ويبدأ بـ : بحيث يتخطى cmd ذلك مثل أي سطر آخر يبدأ بـ :.

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

اعتمادًا على احتياجاتك أو أسلوب الترميز ، قد يكون تشفير cmd و sh منطقيًا. استخدام heredocs هو أحد الأساليب لأداء مثل هذا التداخل. ومع ذلك ، يمكن تمديد هذا باستخدام 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%

يمكن إجراء التعليقات العامة ، بالطبع ، مع تسلسل الأحرف : # أو :;#. تعتبر المساحة أو الفاصلة المنقوطة ضرورية لأن sh تعتبر # جزءًا من اسم الأمر إذا لم يكن الحرف الأول لمعرف. على سبيل المثال ، قد ترغب في كتابة تعليقات عالمية في الأسطر الأولى من الملف الخاص بك قبل استخدام الأسلوب GOTO لتقسيم الشفرة. بعد ذلك يمكنك إبلاغ القارئ عن سبب كتابة البرنامج النصي بشكل غريب للغاية:

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

وبالتالي ، فإن بعض الأفكار والطرق لإنجاز sh و cmd- نصوص متوافقة مع أي آثار جانبية خطيرة بقدر ما أعرف (وبدون امتلاك cmd output '#' is not recognized as an internal or external command, operable program or batch file.).

75
binki

يمكنك تجربة هذا:

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

ربما ستحتاج إلى استخدام /r/n كسطر جديد بدلاً من نمط unix. إذا كنت أتذكر أن السطر الجديد من unix لا يتم تفسيره كسطر جديد بواسطة البرامج النصية .bat. الطريقة الأخرى هي إنشاء ملف #.exe في المسار الذي يعمل به لا تفعل شيئًا مماثلاً لإجابتي هنا: هل من الممكن تضمين VBScript وتنفيذه داخل ملف دفعي دون استخدام ملف مؤقت؟

EDIT

إجابة binki مثالية تقريبًا ولكن لا يزال من الممكن تحسينها:

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

echo $PATH

يستخدم مرة أخرى خدعة : والتعليق متعدد الأسطر. يبدو أن cmd.exe (على الأقل في windows10) يعمل دون مشاكل مع EOLs بنمط unix ، لذا تأكد من تحويل البرنامج النصي إلى تنسيق linux. (لقد تم استخدام نفس الطريقة قبل هنا و هنا ). على الرغم من استخدام Shebang لا يزال سينتج إخراج زائدة عن الحاجة ...

19
npocmaka

أردت التعليق ، لكن يمكنني فقط إضافة إجابة في الوقت الحالي.

التقنيات المقدمة ممتازة وأستخدمها أيضًا.

من الصعب الاحتفاظ بملف يحتوي على نوعين من فواصل الأسطر الموجودة فيه ، وهما /n لجزء bash و /r/n لجزء windows. يحاول معظم المحررين تطبيق مخطط فاصل أسطر شائع عن طريق تخمين نوع الملف الذي تقوم بتحريره. كما أن معظم طرق نقل الملف عبر الإنترنت (خاصة كملف نصي أو نصي) ستقوم بغسل فواصل الأسطر ، لذلك يمكنك البدء بنوع واحد من فاصل الأسطر وينتهي الأمر بالنوع الآخر. إذا قمت بإجراء افتراضات حول فواصل الأسطر ثم أعطيت البرنامج النصي لشخص آخر لاستخدامه ، فقد يجدون أنه لا يعمل لصالحهم.

المشكلة الأخرى هي أنظمة الملفات المحملة على الشبكة (أو الأقراص المدمجة) التي يتم مشاركتها بين أنواع مختلفة من الأنظمة (خاصةً حيث لا يمكنك التحكم في البرنامج المتاح للمستخدم).

لذلك يجب على المرء استخدام فاصل أسطر DOS الخاص بـ /r/n وأيضًا حماية البرنامج النصي bash من DOS /r بوضع تعليق في نهاية كل سطر (#). لا يمكنك أيضًا استخدام عمليات متابعة الأسطر في bash لأن /r سيتسبب في كسرها.

وبهذه الطريقة كل من يستخدم البرنامج النصي ، وفي أي بيئة ، سوف يعمل بعد ذلك.

يمكنني استخدام هذه الطريقة مع صنع Makefiles المحمولة!

11
Brian Tompsett - 汤莱恩

يعمل التالي بالنسبة لي دون أي أخطاء أو رسائل خطأ مع Bash 4 و Windows 10 ، على عكس الإجابات المذكورة أعلاه. أسمي الملف "أيا كان. cmd" ، افعل chmod +x لجعله قابلاً للتنفيذ في نظام التشغيل linux ، ولجعله يحتوي على نهايات سطر يونيكس (dos2unix) للحفاظ على هدوء bash.

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

هناك عدة طرق لتنفيذ الأوامر المختلفة على bash و cmd باستخدام البرنامج النصي نفسه.

cmd سوف يتجاهل الخطوط التي تبدأ بـ :; ، كما هو مذكور في الإجابات الأخرى. سوف يتجاهل أيضًا السطر التالي إذا انتهى السطر الحالي بالأمر rem ^ ، حيث أن حرف ^ سيهرب من فاصل الأسطر وسيتم التعامل مع السطر التالي كتعليق بواسطة rem.

أما بالنسبة لجعل bash فتجاهل أسطر cmd ، فهناك عدة طرق. لقد عدّدت بعض الطرق للقيام بذلك دون كسر أوامر cmd:

أمر # غير موجود (غير مستحسن)

إذا لم يكن هناك أمر # متاح في cmd عند تشغيل البرنامج النصي ، فيمكننا القيام بذلك:

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

يجعل الحرف # في بداية السطر cmdbash يعامل هذا السطر كتعليق.

يتم استخدام حرف # في نهاية السطر bash للتعليق على حرف \r ، كما أشار Brian Tompsett في إجابته . بدون ذلك ، سوف يرسل bash خطأ إذا كان الملف يحتوي على نهايات سطر \r\n ، مطلوب بواسطة cmd.

من خلال القيام بـ # 2>nul ، نخدع cmd لتجاهل خطأ بعض أوامر # غير الموجودة ، مع الاستمرار في تنفيذ الأمر التالي.

لا تستخدم هذا الحل إذا كان هناك أمر # متاحًا في PATH أو إذا لم يكن لديك سيطرة على الأوامر المتاحة لـ cmd.


استخدام echo لتجاهل حرف # على cmd

يمكننا استخدام echo مع إعادة توجيه الإخراج إلى إدراج أوامر cmd في منطقة التعليق bash:

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

نظرًا لأن حرف # ليس له معنى خاص على cmd ، فيتم التعامل معه كجزء من النص إلى echo. كل ما كان علينا القيام به هو إعادة توجيه إخراج الأمر echo وإدخال أوامر أخرى بعده.


ملف #.bat فارغ

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

يقوم سطر echo >/dev/null # 1>nul 2> #.bat بإنشاء ملف #.bat فارغ أثناء وجوده في cmd (أو استبدال #.bat الموجود ، إن وجد) ، ولا يفعل شيئًا على bash.

سيتم استخدام هذا الملف بواسطة سطر (سطور) cmd الذي يتبعه حتى إذا كان هناك بعض أوامر # الأخرى في PATH.

يحذف الأمر del #.bat في رمز cmd- الملف الذي تم إنشاؤه. ما عليك سوى القيام بذلك على آخر سطر cmd.

لا تستخدم هذا الحل إذا كان ملف #.bat يمكن أن يكون في دليل العمل الحالي ، حيث سيتم مسح هذا الملف.


موصى به: استخدام here-document لتجاهل أوامر cmd على bash

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

من خلال وضع حرف ^ في نهاية السطر cmd ، فإننا نهرب من فاصل الأسطر ، وباستخدام : كمحدد مستند هنا ، لن يكون لمحتويات سطر المحدد أثر في cmd. وبهذه الطريقة ، لن ينفذ cmd سوى السطر الخاص به بعد انتهاء السطر : ، مع اتباع نفس سلوك bash.

إذا كنت تريد أن يكون لديك خطوط متعددة على كلا النظامين الأساسيين وتنفيذها فقط في نهاية الكتلة ، يمكنك القيام بذلك:

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

طالما لا يوجد سطر cmd مع here-document delimiter تمامًا ، فيجب أن يعمل هذا الحل. يمكنك تغيير here-document delimiter إلى أي نص آخر.


في جميع الحلول المقدمة ،لن يتم تنفيذ الأوامر إلا بعد السطر الأخير، مما يجعل سلوكهم ثابتًا إذا فعلوا نفس الشيء على كلا النظامين الأساسيين.

يجب حفظ هذه الحلول في الملفات ذات \r\n كفواصل أسطر ، وإلا فلن تعمل على cmd.

2
Tex Killer

يمكنك مشاركة المتغيرات:

:;SET() { eval $1; }

SET var=value

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

يبدو أن الإجابات السابقة تغطي جميع الخيارات إلى حد كبير وساعدتني كثيرًا. أقوم بتضمين هذه الإجابة هنا فقط لشرح الآلية التي استخدمتها لتضمين كل من البرنامج النصي Bash و Windows CMD النصي في نفس الملف.

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.

ملخص

في لينكس

سيتم تجاهل السطر الأول (echo >/dev/null # >nul & GOTO WINDOWS & rem ^) وسيتدفق البرنامج النصي عبر كل سطر يتبعه مباشرة حتى يتم تنفيذ الأمر exit 0. بمجرد الوصول إلى exit 0 ، سينتهي تنفيذ البرنامج النصي ، متجاهلاً أوامر Windows الموجودة تحته.

في ويندوز

سيقوم السطر الأول بتنفيذ الأمر GOTO WINDOWS ، وتخطي أوامر Linux التي تليها مباشرة ومواصلة التنفيذ في سطر :WINDOWS.

إزالة النقل العوائد في ويندوز

نظرًا لأنني كنت أقوم بتحرير هذا الملف في نظام التشغيل Windows ، فقد اضطررت إلى إزالة نتائج النقل (\ r) بشكل منتظم من أوامر Linux أو حصلت على نتائج غير طبيعية عند تشغيل جزء Bash. للقيام بذلك ، فتحت الملف في Notepad ++ وقمت بما يلي:

  1. قم بتشغيل الخيار لعرض نهاية أحرف السطر (View> Show Symbol> Show End of Line). ستظهر عمليات إرجاع الأحرف كأحرف CR.

  2. قم بإجراء بحث واستبدال (Search> Replace...) وتحقق من خيار Extended (\n, \r, \t, \0, \x...).

  3. اكتب \r في حقل Find what : وقم بإفراغ حقل Replace with : حتى لا يوجد شيء فيه.

  4. بدءًا من أعلى الملف ، انقر فوق الزر Replace حتى تتم إزالة جميع أحرف إرجاع الأحرف (CR) من الجزء العلوي من Linux. تأكد من ترك أحرف الإرجاع (CR) لجزء Windows.

يجب أن تكون النتيجة أن ينتهي كل أمر Linux في سطر تغذية (LF) وينتهي كل أمر من أوامر Windows في حرف إرجاع وسطر (CRLF).

1
A-Diddy

أنا استخدم هذه التقنية لإنشاء ملفات جرة runnable. نظرًا لأن ملف jar/Zip يبدأ من رأس Zip ، يمكنني وضع برنامج نصي عالمي لتشغيل هذا الملف في الأعلى:

#!/usr/bin/env sh
@ 2>/dev/null # 2>nul & echo off
:; alias ::=''
:: exec Java -jar $Java_OPTS "$0" "[email protected]"
:: exit
Java -jar %Java_OPTS% "%~dpnx0" %*
exit /B
  • السطر الأول يصدر صدى في كمد ولا يطبع أي شيء على sh. وذلك لأن @ in sh يلقي خطأ يتم توجيهه إلى /dev/null وبعد ذلك يبدأ التعليق. في cmd ، فشل توجيه الإخراج إلى /dev/null لأنه لم يتم التعرف على الملف على النوافذ ولكن لأن النوافذ لا تكتشف # كتعليق ، يتم توجيه الخطأ إلى nul. ثم يفعل صدى قبالة. نظرًا لأن السطر بأكمله مسبوقًا بـ @ ، فإنه لا يحصل على نسخة مطبوعة على cmd.
  • يحدد الثاني :: ، الذي يبدأ تعليقًا في cmd ، ليتم إيقافه في sh. هذا له فائدة أن :: لا يعيد ضبط $? إلى 0. يستخدم خدعة ":;".
  • الآن يمكنني إعداد أوامر sh بـ :: ويتم تجاهلها في cmd
  • في :: exit ينتهي البرنامج النصي sh ويمكنني كتابة أوامر cmd
  • فقط السطر الأول (Shebang) يمثل مشكلة في cmd لأنه سيطبع command not found. عليك أن تقرر ما إذا كنت في حاجة إليها أم لا.
0
LolHens