Hem Windows toplu işleminde hem de Linux Bash'te çalıştırılacak tek komut dosyası mı?


97

Hem Windows'ta (.bat olarak kabul edilir) hem de Linux'ta (Bash aracılığıyla) çalışan tek bir komut dosyası yazmak mümkün müdür?

Her ikisinin de temel sözdizimini biliyorum ama çözemedim. Muhtemelen bazı Bash'in belirsiz sözdiziminden veya bazı Windows toplu işlemcisi hatalarından yararlanabilir.

Yürütme komutu, diğer komut dosyasını yürütmek için yalnızca tek bir satır olabilir.

Motivasyon, hem Windows hem de Linux için yalnızca tek bir uygulama önyükleme komutuna sahip olmaktır.

Güncelleme: Sistemin "yerel" kabuk betiğine duyulan ihtiyaç, doğru yorumlayıcı sürümünü seçmesi, iyi bilinen belirli ortam değişkenlerine uyması vb. Gerektiğidir. CygWin gibi ek ortamlar kurmak tercih edilmez - konsepti korumak istiyorum " indir ve çalıştır ".

Windows için dikkate alınması gereken diğer tek dil, 98'den beri varsayılan olarak önceden ayarlanmış olan Windows Komut Dosyası Sistemi - WSH'dir.


9
Perl veya Python'a bakın. Bunlar her iki platformda da kullanılabilen komut dosyası dilleridir.
a_horse_with_no_name

groovy, python, Ruby kimse var mı? stackoverflow.com/questions/257730/…
Kalpesh Soni

Groovy'nin varsayılan olarak tüm sistemlerde olması harika olurdu, bunu bir kabuk komut dosyası dili olarak kabul ediyorum. Belki bir gün :)
Ondra Žižka

1
Ayrıca bakınız: github.com/BYVoid/Batsh
Dave Jarvis

2
Bunun için ikna edici bir kullanım örneği öğreticilerdir - insanlara bunu açıklamak zorunda kalmadan "bu küçük komut dosyasını çalıştır" diyebilmek için "Windows kullanıyorsanız, bu küçük komut dosyasını kullanın, ancak Linux veya Mac kullanıyorsanız, şunu deneyin bunun yerine, Windows'ta olduğum için aslında test etmedim. " Ne yazık ki Windows, cpyerleşik gibi temel unix benzeri komutlara sahip değildir veya bunun tersi de geçerlidir, bu nedenle iki ayrı komut dosyası yazmak, burada gösterilen gelişmiş tekniklerden pedagojik olarak daha iyi olabilir.
Qwertie

Yanıtlar:


92

Yaptığım şey cmd'nin etiket sözdizimini yorum işaretçisi olarak kullanmak oldu . Etiket karakteri, iki nokta üst üste ( :), trueçoğu POSIXish kabuğuna eşdeğerdir . Etiket karakterini hemen bir karakterde kullanılamayan başka bir karakterle takip ederseniz GOTO, cmdkomut dosyanızı yorumlamak cmdkodunuzu etkilememelidir .

Hack, " :;" karakter dizisinden sonra kod satırları koymaktır . Çoğunlukla tek satırlık komut dosyaları yazıyorsanız veya duruma göre, shbirçok satır için bir satır yazabiliyorsanız cmd, aşağıdakiler iyi olabilir. Her türlü kullanımı unutmayın $?sonraki kolon önce olmalıdır :çünkü :resetlemelerin $?0'a.

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

Çok uydurma bir koruma örneği $?:

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

Atlayarak için başka bir fikir cmdkodu kullanmaktır heredoc böylece shdavranır cmdkullanılmayan bir dize gibi kod cmdyorumladığını o. Bu durumda, yorumlayıcımızın sınırlayıcısının hem alıntı shyapıldığından (ile çalışırken içeriği üzerinde herhangi bir yorum yapmayı durdurmak için) hem de ile başladığından emin oluruz sh, :böylece cmdile başlayan diğer satırlar gibi atlar :.

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

İhtiyaçlarınıza veya kodlama stilinize bağlı olarak, taramalı cmdve shkod anlamlı olabilir veya olmayabilir. Yorumlu metinler kullanmak, bu tür taramaları gerçekleştirmenin bir yöntemidir. Ancak bu GOTOteknikle genişletilebilir :

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

Elbette evrensel yorumlar, karakter dizisi : #veya :;#. Boşluk veya noktalı virgül gereklidir çünkü bir tanımlayıcının ilk karakteri değilse bir komut adının parçası olarak shkabul #edilir. Örneğin, GOTOkodunuzu bölme yöntemini kullanmadan önce dosyanızın ilk satırlarına evrensel yorumlar yazmak isteyebilirsiniz . O zaman okuyucunuza senaryonuzun neden bu kadar tuhaf yazıldığını söyleyebilirsiniz:

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

Bu nedenle, bildiğim kadarıyla (ve çıktı olmadan) ciddi yan etkileri olmayan shve cmduyumlu komut dosyalarını gerçekleştirmenin bazı fikirleri ve yolları .cmd'#' is not recognized as an internal or external command, operable program or batch file.


5
Bunun, komut dosyasının .cmduzantısına sahip olmasını gerektirdiğine dikkat edin , aksi takdirde Windows'ta çalışmayacaktır. Herhangi bir çözüm var mı?
jviotti

1
Toplu iş dosyaları yazıyorsanız, yalnızca adlandırmanızı .cmdve çalıştırılabilir olarak işaretlemenizi öneririm . Bu şekilde, komut dosyasını varsayılan kabuktan Windows veya unix üzerinde çalıştırmak işe yarar (yalnızca bash ile test edilir). Cmd'yi yüksek sesle şikayet etmeden bir shebang'i dahil etmenin bir yolunu düşünemediğim için, komut dosyasını her zaman kabuk yoluyla çağırmalısınız. Yani, kullandığınızdan emin olmak execlp()veya execvp()(Sanırım system()ziyade bu sizin için “doğru” yolu yok) execl()ya da execv()(bu sonuncular işlevleri olacak shebangs ile komut sadece iş onlar OS daha doğrudan gitmek için).
binki

Dosya uzantılarıyla ilgili tartışmayı atladım çünkü taşınabilir kodumu bağımsız toplu iş dosyaları yerine kabuklarda (örn. <Exec/>MSBuild Görevi ) yazıyordum . Belki gelecekte biraz zamanım olursa, cevabı bu yorumlardaki bilgilerle güncellerim…
binki

23

bunu deneyebilirsin:

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

Muhtemelen /r/nbir unix stili yerine yeni bir satır olarak kullanmanız gerekecek. Unix'in yeni satırının komut .batdosyaları tarafından yeni bir satır olarak yorumlanmadığını hatırlıyorsam, başka bir yol #.exeda yolda hiçbir şey yapmayan bir dosya oluşturmaktır . Buradaki cevabımla benzer şekilde: VBScript'i geçici bir dosya kullanmadan bir toplu iş dosyasına gömmek ve yürütmek mümkün müdür?

DÜZENLE

Binki cevabı neredeyse mükemmel ama hala geliştirilebilir:

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

echo $PATH

Yine :hileyi ve çok satırlı açıklamayı kullanır. Cmd.exe (en azından windows10'da) gibi görünüyor, unix tarzı EOL'lerle sorunsuz çalışıyor, bu yüzden betiğinizin linux formatına dönüştürüldüğünden emin olun. (aynı yaklaşımın daha önce burada ve burada kullanıldığı görülmüştür ). Shebang kullanmak yine de gereksiz çıktı üretecek olsa da ...


3
/r/nsatırların sonunda her satırı bir #(ie #/r/n) ile sonlandırmadığınız sürece bash betiğinde birçok baş ağrısına neden olacaktır - bu şekilde \ryorumun bir parçası olarak ele alınacak ve yok sayılacaktır.
Gordon Davisson

2
Aslında, komut tarafından bulunmayan komutla # 2>NUL & ECHO Welcome to %COMSPEC%.ilgili hatayı gizlemeyi başardığını görüyorum . Bu teknik, yalnızca (örneğin a'da) tarafından yürütülecek bağımsız bir satır yazmanız gerektiğinde kullanışlıdır . #cmdcmdMakefile
binki

11

Yorum yapmak istedim, ancak şu anda sadece bir cevap ekleyebiliyorum.

Verilen teknikler mükemmel ve onları da kullanıyorum.

/nBash bölümü ve /r/nwindows bölümü için olmak üzere, içinde iki tür satır sonu bulunan bir dosyayı saklamak zordur . Çoğu düzenleyici, ne tür bir dosyayı düzenlediğinizi tahmin ederek ortak bir satır sonu şeması dener ve uygular. Ayrıca, dosyayı internet üzerinden aktarmanın çoğu yöntemi (özellikle bir metin veya komut dosyası olarak) satır sonlarını aklayacaktır, böylece bir tür satır sonu ile başlayıp diğeriyle sonlandırabilirsiniz. Satır sonları hakkında varsayımlar yaptıysanız ve sonra komut dosyanızı kullanması için başka birine verdiyseniz, onlar için çalışmadığını görebilirler.

Diğer sorun, farklı sistem türleri arasında paylaşılan ağa bağlı dosya sistemleridir (veya CD'ler) (özellikle kullanıcının kullanabileceği yazılımı kontrol edemediğiniz yerlerde).

Bu nedenle, DOS'un satır sonu kullanılmalı /r/nve ayrıca bash betiğini DOS'tan /rher satırın sonuna bir yorum ekleyerek ( #) korumalıdır . Ayrıca bash'ta satır devam ettirmelerini kullanamazsınız çünkü bu /ronların kırılmasına neden olur.

Bu şekilde, senaryoyu kim kullanırsa kullansın ve hangi ortamda olursa olsun o zaman çalışacaktır.

Bu yöntemi taşınabilir Makefiles yapmakla birlikte kullanıyorum!


6

Aşağıdakiler, yukarıdaki yanıtların aksine, Bash 4 ve Windows 10'da herhangi bir hata veya hata mesajı olmadan benim için çalışır. Dosyayı "ne olursa olsun.cmd" olarak adlandırıyorum, chmod +xlinux'ta çalıştırılabilir hale getirmek için yapıyorum ve dos2unixbash'ı sessiz tutmak için unix satır sonlarına ( ) sahip olmasını sağlıyorum.

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

3

Aynı komut dosyası üzerinde bashve cmdaynı komut dosyasıyla farklı komutları çalıştırmanın birkaç yolu vardır .

cmd:;diğer yanıtlarda belirtildiği gibi ile başlayan satırları yok sayacaktır . Ayrıca, mevcut satır komutla biterse sonraki satırı da yok sayar rem ^, çünkü ^karakter satır sonundan kaçar ve sonraki satır tarafından yorum olarak değerlendirilir rem.

Çizgileri bashgörmezden gelmeye gelince cmd, birden fazla yol var. cmdKomutları bozmadan bunu yapmanın bazı yollarını sıraladım :

Varolmayan #komut (önerilmez)

Betik çalıştırıldığında herhangi bir #komut mevcut değilse cmd, bunu yapabiliriz:

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

#Başında karakter cmdçizgisi markaların bashbir yorum olarak bu çizgiyi davranın.

#Sonundaki karakter bashhattına açıklama için kullanılır \rBrian Tompsett işaret ettiği gibi, karakter onun cevabını . Bu olmadan bash, dosyanın \r\ngerektirdiği satır sonları varsa bir hata atar cmd.

Bunu yaparak # 2>nul, takip eden komutu yürütmeye devam ederken, cmdvar olmayan bazı #komutların hatasını görmezden gelmek için kandırıyoruz .

#Üzerinde bir komut PATHvarsa veya kullanabileceğiniz komutlar üzerinde hiçbir kontrolünüz yoksa bu çözümü kullanmayın cmd.


Üzerindeki karakteri echoyok saymak için kullanma#cmd

echoYeniden yönlendirilmiş çıktı ile yorumlanan alana cmdkomutlar eklemek için kullanabiliriz bash:

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

Yana #karakter üzerinde anlam hiçbir special vardır cmd, bu metnin bir parçası olarak kabul edilir echo. Tek yapmamız gereken echokomutun çıktısını yeniden yönlendirmek ve ardından başka komutlar eklemek.


Boş #.batdosya

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

echo >/dev/null # 1>nul 2> #.batHat boş oluşturur #.batiken dosyayı cmd(veya cümledeki varolan #.batvarsa) ve üzerinde ise hiçbir şey yapmaz bash.

Bu dosya, üzerinde cmdbaşka bir #komut olsa bile izleyen satır (lar) tarafından kullanılacaktır PATH.

del #.batÜzerinde komut cmde özgü kod oluşturulduğu dosyayı siler. Bunu sadece son cmdsatırda yapmanız gerekir .

#.batMevcut çalışma dizininizde bir dosya bulunma olasılığı varsa bu çözümü kullanmayın , çünkü bu dosya silinecektir.


Önerilen: buradaki belgeyi kullanarak cmdkomutları yoksaymakbash

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

^Karakteri cmdsatırın sonuna yerleştirerek satır sonundan kaçarız :ve burayı-belge sınırlayıcı olarak kullanarak sınırlayıcı satır içeriklerinin hiçbir etkisi olmaz cmd. Bu şekilde, cmdsatırını yalnızca satır bittikten sonra çalıştıracak ve :aynı davranışa sahip olacaktır bash.

Her iki platformda birden fazla satıra sahip olmak ve bunları yalnızca bloğun sonunda çalıştırmak istiyorsanız, bunu yapabilirsiniz:

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

cmdTam olarak bir çizgi olmadığı sürece here-document delimiter, bu çözüm işe yaramalıdır. Başka here-document delimiterherhangi bir metne geçebilirsiniz .


Sunulan tüm çözümlerde, komutlar yalnızca son satırdan sonra çalıştırılır ve her iki platformda da aynı şeyi yaparlarsa davranışlarını tutarlı hale getirir.

Bu çözümler \r\nsatır sonları ile dosyalara kaydedilmelidir , aksi takdirde üzerinde çalışmazlar cmd.


Geç olması hiç olmamasından iyidir ... bu bana diğer cevaplardan daha çok yardımcı oldu. Teşekkürler!!
A-Diddy

3

Değişkenleri paylaşabilirsiniz:

:;SET() { eval $1; }

SET var=value

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

2

Önceki cevaplar hemen hemen tüm seçenekleri kapsıyor ve bana çok yardımcı oldu. Bu yanıtı buraya hem Bash komut dosyasını hem de Windows CMD komut dosyasını aynı dosyaya eklemek için kullandığım mekanizmayı göstermek için ekliyorum.

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.

Özet

Linux'ta

İlk satır ( echo >/dev/null # >nul & GOTO WINDOWS & rem ^) yok sayılacak ve exit 0komut yürütülene kadar komut dosyası hemen ardından gelen her satırdan geçecektir . Bir kez exit 0ulaşıldığında, komut dosyası yürütme, altındaki Windows komutlarını yok sayarak sona erecektir.

Windows'ta

İlk satır GOTO WINDOWSkomutu çalıştıracak, hemen ardından gelen Linux komutlarını atlayacak ve :WINDOWSsatırda yürütmeye devam edecektir .

Windows'ta Taşıyıcı İadelerini Kaldırma

Bu dosyayı Windows'ta düzenlediğim için, Linux komutlarından satır başlarını (\ r) sistematik olarak kaldırmak zorunda kaldım, aksi takdirde Bash bölümünü çalıştırırken anormal sonuçlar aldım. Bunu yapmak için, dosyayı Notepad ++ ile açtım ve şunları yaptım:

  1. Çizgi karakterlerin sonunu izlemek için seçenek açın ( View> Show Symbol> Show End of Line). Ardından satır başları CRkarakter olarak gösterilecektir .

  2. Bul ve Değiştir ( Search> Replace...) yapın ve Extended (\n, \r, \t, \0, \x...)seçeneği işaretleyin.

  3. Tür \riçinde Find what :dışarı alan ve boş Replace with :alanda bu yüzden de bir şey yok.

  4. Dosyanın üstünden başlayarak , üstteki Linux bölümünden Replacetüm carriage return ( CR) karakterleri kaldırılıncaya kadar düğmeyi tıklayın . CRWindows bölümü için satır başı ( ) karakterlerini bıraktığınızdan emin olun .

Sonuç, her Linux komutunun yalnızca bir satır besleme ( LF) ile bitmesi ve her Windows komutunun bir satır başı ve satır besleme ( CR LF) ile bitmesi olmalıdır .


Notepad ++ 'da Düzen> EOL dönüşümü> LF'yi seçin , bu tür değişiklikler yapmaya gerek yoktur. Ve bunu her seferinde yapmaya gerek yoktur, sadece Ayarlar> Tercihler'de gerekirse LF'yi varsayılan olarak ayarlayın. Aksi takdirde Windows'ta CRLF dönüşümü yapmanın birçok yolu vardır
phuclv

2

Bu tekniği çalıştırılabilir jar dosyaları oluşturmak için kullanıyorum. Jar / zip dosyası zip başlığında başladığından, bu dosyayı en üste çalıştırmak için evrensel bir komut dosyası koyabilirim:

#!/usr/bin/env sh\n
@ 2>/dev/null # 2>nul & echo off & goto BOF\r\n
:\n
<shell commands go here with \n line endings>
exit\n
\r\n
:BOF\r\n
<cmd commands go here with \r\n line endings>\r\n
exit /B %errorlevel%\r\n
}

Farklı platformlarda sorunlara neden olabileceğinden, satır sonlarını yukarıda belirtildiği gibi ayarlamak önemlidir. Ayrıca, atlama etiketinin etrafında uygun satır sonları eksikse, goto deyimi bazı durumlarda düzgün çalışmayabilir.

Yukarıdaki teknik şu anda kullandığım şey. Aşağıda, derinlemesine bir açıklama ile eski bir sürüm bulunmaktadır:

#!/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
  • İlk satır cmd'de yankılanıyor ve sh üzerine hiçbir şey yazdırmıyor. Bunun nedeni, @in'in borulanan bir hatayı atması /dev/nullve bundan sonra bir yorum başlamasıdır. Cmd'de /dev/nulldosya pencerelerde tanınmadığından boru başarısız olur, ancak pencereler #bir açıklama olarak algılamadığı için hata iletilir nul. Sonra yankı yapıyor. Tüm satırın önünde bir satır olduğundan, @cmd'de printet almaz.
  • İkincisi ::, cmd'de bir yorum başlatan sh in noop'u tanımlar . Bu yararı vardır ::sıfırlanmaz $?için 0. Bu "kullanan :;hüner bir etikettir."
  • Şimdi sh komutlarının başına ekleyebilirim ve cmd'de ::yok sayılırlar
  • Açık :: exitsh komut biter ve ben cmd komutları yazabilir
  • Yalnızca ilk satır (shebang), yazdıracağı için cmd'de sorunludur command not found. İhtiyacınız olup olmadığına kendiniz karar vermelisiniz.

0

Python paket kurulum betiklerimden bazıları için buna ihtiyacım vardı. Sh ve bat dosyası arasındaki çoğu şey aynıdır ancak hata işleme gibi birkaç şey farklıdır. Bunu yapmanın bir yolu şudur:

common.inc
----------
common statement1
common statement2

Sonra bunu bash betiğinden çağırırsınız:

linux.sh
--------
# do linux specific stuff
...
# call common code
source common.inc

Windows toplu iş dosyası şuna benzer:

windows.bat
-----------
REM do windows specific things
...
# call common code
call common.inc


-6

Xml sözdizimi (Java tabanlı) ile Ant veya Maven gibi platformdan bağımsız bir oluşturma araçları vardır. Böylece, tüm komut dosyalarınızı Ant veya Maven'de yeniden yazabilir ve os türüne rağmen çalıştırabilirsiniz. Veya işletim sistemi türünü analiz edecek ve uygun bat veya bash betiğini çalıştıracak Ant sarmalayıcı betiği oluşturabilirsiniz.


5
Bu, bu araçların büyük bir kötüye kullanılması gibi görünüyor. Asıl sorudan (yerel kabukta çalışan) kaçınmak istiyorsanız, o zaman python gibi evrensel bir komut dosyası dili önermelisiniz, uygun bir komut dosyası dili yerine kötüye kullanılabilecek bir oluşturma aracı değil.
ArtOfWarfare
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.