Windows Komut Yorumlayıcısı (CMD.EXE) komut dosyalarını nasıl ayrıştırır?


142

Ben koştum ss64.com Windows Komut Tercüman çalışacağı toplu scriptlerinin nasıl dair iyi bir yardım sağlayan.

Ancak, toplu betiklerin gramerini , işlerin nasıl genişlediğini veya genişlemediğini ve nasıl kaçacağını iyi bir şekilde bulamadım .

İşte çözemediğim örnek sorular:

  • Teklif sistemi nasıl yönetilir? Bir TinyPerl betiği
    ( foreach $i (@ARGV) { print '*' . $i ; }) yaptım , derledim ve şöyle çağırdım:
    • my_script.exe "a ""b"" c" → çıktı *a "b*c
    • my_script.exe """a b c""" → çıktı *"a*b*c"
  • Dahili echokomut nasıl çalışır? Bu komutun içinde ne genişledi?
  • Neden for [...] %%Idosya komut dosyalarında, ancak for [...] %Ietkileşimli oturumlarda kullanmam gerekiyor?
  • Kaçış karakterleri nedir ve hangi bağlamda? Yüzde işareti nasıl kaçar? Örneğin, %PROCESSOR_ARCHITECTURE%kelimenin tam anlamıyla yankılanabilirim ? İşe echo.exe %""PROCESSOR_ARCHITECTURE%yaradığını buldum, daha iyi bir çözüm var mı?
  • Çiftler nasıl %eşleşir? Misal:
    • set b=a, echo %a %b% c%%a a c%
    • set a =b, echo %a %b% c%bb c%
  • Bu değişken çift tırnak içeriyorsa, bir değişkenin bir komuta tek bir bağımsız değişken olarak geçmesini nasıl sağlayabilirim?
  • setKomut kullanılırken değişkenler nasıl saklanır ? Örneğin, yaparsam set a=a" bve sonra echo.%a%alırsam a" b. Ancak echo.exeUnxUtils kullanırsanız, ben olsun a b. Nasıl %a%farklı bir şekilde genişler?

Işıklarınız için teşekkür ederim.


Yanıtlar:


200

Toplu betiklerin gramerini araştırmak için deneyler yaptık. Ayrıca, toplu iş ve komut satırı modu arasındaki farkları araştırdık.

Toplu Satır Ayrıştırıcı:

Toplu iş dosyası satır ayrıştırıcısındaki aşamalara kısa bir genel bakış:

Aşama 0) Okuma Satırı:

Aşama 1) Yüzde Genişleme:

Aşama 2) Özel karakterleri işleyin, önbelleğe alınmış bir komut bloğu belirtin ve oluşturun: Bu, tırnak işaretleri, özel karakterler, belirteç sınırlayıcılar ve düzeltme işareti kaçışları gibi şeylerden etkilenen karmaşık bir işlemdir.

Aşama 3) Ayrıştırılan komutları yankılama Yalnızca komut bloğu başlamamışsa @ve önceki adımın başında ECHO AÇIK ise.

Aşama 4) FOR %Xdeğişken genişletme: Sadece bir FOR komutu aktifse ve DO'dan sonraki komutlar işleniyorsa.

Aşama 5) Gecikmeli Genişleme: Yalnızca gecikmeli genişleme etkinse

Aşama 5.3) Boru işleme: Yalnızca komutlar bir borunun her iki tarafındaysa

Aşama 5.5) Yeniden Yönlendirme Yürütme:

Aşama 6) ÇAĞRI işleme / Düzeltme çiftleme: Yalnızca komut belirteci ÇAĞRI ise

Aşama 7) Yürüt: Komut yürütülür


Her aşama için detaylar:

Aşağıda açıklanan aşamaların yalnızca toplu ayrıştırıcının nasıl çalıştığının bir modeli olduğunu unutmayın. Gerçek cmd.exe iç bileşenleri bu aşamaları yansıtmayabilir. Ancak bu model, toplu komut dosyalarının davranışını tahmin etmede etkilidir.

Aşama 0) Okuma Satırı: İlk önce giriş satırını okuyun <LF>.

  • Komut olarak ayrıştırılacak satırı okurken, <Ctrl-Z>(0x1A) <LF>(LineFeed 0x0A) olarak okunur
  • Bir için tararken GOTO veya ÇAĞRI hatları okuduğunda: etiket, <Ctrl-Z>kendisi olarak kabul edilir - bu bir değil dönüştürüldü<LF>

Aşama 1) Yüzde Genişleme:

  • Bir çiftin %%yerine tek bir%
  • Bağımsız değişken genişlemesi ( %*, %1, %2vs.)
  • Genişlemesi, %var%var yoksa hiçbir şeyle değiştirmeyin
  • Çizgi ilk başta genişleme <LF>dahilinde kesilir%var%
  • Tam bir açıklama için bunun ilk yarısını dbenham'dan okuyun Aynı konu: Yüzde Faz

Aşama 2) Özel karakterleri işleyin, önbelleğe alınmış bir komut bloğu belirtin ve oluşturun: Bu, tırnak işaretleri, özel karakterler, belirteç sınırlayıcılar ve düzeltme işareti kaçışları gibi şeylerden etkilenen karmaşık bir işlemdir. Aşağıda, bu sürecin bir yaklaşımı yer almaktadır.

Bu aşamada önemli olan kavramlar vardır.

  • Bir belirteç, birim olarak kabul edilen bir karakter dizisidir.
  • Jetonlar belirteç sınırlayıcılarla ayrılır. Standart belirteç sınırlayıcılar <space> <tab> ; , = <0x0B> <0x0C>ve <0xFF>
    Ardışık belirteç sınırlayıcılar tek olarak ele alınır - belirteç sınırlayıcılar arasında boş belirteç yoktur
  • Alıntılanan bir dizede belirteç sınırlayıcı yok. Alıntılanan dizenin tamamı her zaman tek bir simgenin parçası olarak ele alınır. Tek bir belirteç, tırnak içine alınmış dizelerin ve tırnaksız karakterlerin birleşiminden oluşabilir.

Aşağıdaki karakterler, bağlama bağlı olarak bu aşamada özel bir anlama sahip olabilir: <CR> ^ ( @ & | < > <LF> <space> <tab> ; , = <0x0B> <0x0C> <0xFF>

Her karaktere soldan sağa bakın:

  • Daha <CR>sonra, hiç orada değilmiş gibi kaldırın (garip yönlendirme davranışı hariç )
  • Bir düzeltme ( ^) ise, bir sonraki karakter kaçar ve çıkış yapan düzeltme işareti kaldırılır. Kaçan karakterler tüm özel anlamları kaybeder (hariç <LF>).
  • Bir teklif ( ") ise, teklif bayrağını değiştirin. Teklif bayrağı etkinse, yalnızca o zaman "ve <LF>özeldir. Diğer tüm karakterler, bir sonraki tırnak tırnak işaretini kaldırana kadar özel anlamlarını yitirir. Kapanış teklifinden kaçmak mümkün değildir. Alıntılanan tüm karakterler her zaman aynı jeton içindedir.
  • <LF>her zaman teklif bayrağını kapatır. Diğer davranışlar bağlama göre değişir, ancak tırnak işaretleri hiçbir zaman davranışını değiştirmez <LF>.
    • Kaçtı <LF>
      • <LF> soyulmuş
      • Bir sonraki karakter kaçar. Satır tamponunun sonunda, bir sonraki satır 1 ve 1.5 fazları tarafından okunur ve işlenir ve bir sonraki karakterden kaçmadan önce geçerli olana eklenir. Bir sonraki karakter ise <LF>, o zaman bir değişmez olarak kabul edilir, yani bu süreç özyinelemeli değildir.
    • Çıkışsız <LF>parantez içinde değil
      • <LF> çıkarılır ve geçerli satırın ayrıştırılması sonlandırılır.
      • Satır arabelleğinde kalan karakterler yok sayılır.
    • <LF>FOR IN parantez içindeki bloktan kaçtı
      • <LF> dönüştürülür <space>
      • Satır ara belleğinin sonunda bir sonraki satır okunur ve geçerli satırın sonuna eklenir.
    • <LF>Parantez içine alınmış komut bloğunda kaçış yok
      • <LF>dönüştürülür <LF><space>ve <space>komut bloğunun bir sonraki çizgi parçası olarak kabul edilir.
      • Satır ara belleğinin sonundaysa, sonraki satır okunur ve boşluğa eklenir.
  • Özel karakterlerden biri & | <veya >, boruları, komut birleştirme ve yeniden yönlendirme işlemek için bu noktada satırı bölün.
    • Bir boru ( |) söz konusu olduğunda , her iki taraf da 5.3.
    • Halinde &, &&ya da ||bir komut birleştirme, birleştirme her iki tarafı ayrı komut olarak işlenir.
    • Durumunda <, <<, >ya da >>yönlendirme, yönlendirme maddesi geçici olarak çıkarılmış, ayrıştırılır, ve daha sonra mevcut komutun sonuna eklenen. Yeniden yönlendirme yan tümcesi, isteğe bağlı bir dosya tanıtıcı basamağı, yeniden yönlendirme işleci ve yeniden yönlendirme hedef belirtecinden oluşur.
      • Yeniden yönlendirme işlecinden önce gelen belirteç, tek bir çıkış karakteri olmayan tek basamaksa, basamak yeniden yönlendirilecek dosya tanıtıcısını belirtir. Tanıtıcı belirteci bulunmazsa, çıkış yeniden yönlendirme varsayılanı 1 (stdout) ve giriş yeniden yönlendirme varsayılanı 0 (stdin) olur.
  • İlk (önceki ucuna yeniden yönlendirme hareketli kadar) bu komut için belirteci Eğer başlar @, sonra @özel bir anlamı vardır. ( @başka bir bağlamda özel değildir)
    • Özel @kaldırıldı.
    • ECHO AÇIK ise, bu komut, bu satırda aşağıdaki sıralı komutlarla birlikte, 3. aşama yankısının dışında tutulur. Bir @açıklıktan önce ise (, parantez içindeki tüm blok faz 3 ekodan hariç tutulur.
  • İşlem parantezi (birden çok satırda bileşik ifadeler sağlar):
    • Ayrıştırıcı bir komut belirteci (aramıyorsa, özel değildir.
    • Ayrıştırıcı bir komut belirteci arıyor ve bulursa (, yeni bir bileşik deyimi başlatın ve parantez sayacını artırın
    • Parantez sayacı> 0 ise ), bileşik ifadesini sonlandırır ve parantez sayacını azaltır.
    • Satır ucuna ulaşılırsa ve parantez sayacı> 0 ise, bir sonraki satır bileşik ifadesine eklenir (yeniden faz 0 ile başlar)
    • Parantez sayacı 0 ise ve ayrıştırıcı bir komut arıyorsa , hemen bir belirteç sınırlayıcı, özel karakter, yeni satır veya dosya sonu geldiği sürece )bir REMifadeye benzer şekilde çalışır
      • Dışındaki tüm özel karakterler anlamlarını yitirir ^(satır birleştirme mümkündür)
      • Mantıksal satırın sonuna ulaşıldığında, tüm "komut" atılır.
  • Her komut bir dizi jetona ayrıştırılır. İlk belirteç her zaman bir komut belirteci olarak kabul edilir (özel öğütme yapıldıktan @ve yeniden yönlendirme sona erdikten sonra).
    • Komut belirtecinden önce önde gelen belirteç sınırlayıcılar çıkarılır
    • Komut belirtecini ayrıştırırken (, standart belirteç sınırlayıcılarına ek olarak bir komut belirteci sınırlayıcısı olarak işlev görür
    • Sonraki tokenlerin kullanımı komuta bağlıdır.
  • Çoğu komut, komut belirtecinden sonraki tüm bağımsız değişkenleri tek bir bağımsız değişken belirtecinde birleştirir. Tüm argüman belirteci sınırlayıcıları korunur. Bağımsız değişken seçenekleri genellikle faz 7'ye kadar ayrıştırılmaz.
  • Üç komut özel işlem görür - IF, FOR ve REM
    • IF, bağımsız olarak işlenen iki veya üç ayrı parçaya bölünür. IF yapısındaki bir sözdizimi hatası, önemli bir sözdizimi hatasına neden olacaktır.
      • Karşılaştırma işlemi, aşama 7'ye kadar akan gerçek komuttur
        • Tüm IF seçenekleri aşama 2'de tamamen ayrıştırılır.
        • Ardışık jeton sınırlayıcıları tek bir boşluğa dalar.
        • Karşılaştırma operatörüne bağlı olarak, tanımlanan bir veya iki değer belirteci olacaktır.
      • True komut bloğu, koşuldan sonraki komut kümesidir ve diğer komut blokları gibi ayrıştırılır. ELSE kullanılacaksa, True bloğunun parantez içinde olması gerekir.
      • İsteğe bağlı Yanlış komut bloğu, ELSE'ten sonraki komut kümesidir. Yine, bu komut bloğu normal şekilde ayrıştırılır.
      • Doğru ve Yanlış komut blokları sonraki aşamalara otomatik olarak akmaz. Takip eden işlemleri faz 7 ile kontrol edilir.
    • FOR, DO'dan sonra ikiye bölünür. FOR yapısında bir sözdizimi hatası, önemli bir sözdizimi hatasına neden olacaktır.
      • DO'dan geçen kısım, faz 7'ye kadar akan gerçek FOR yineleme komutudur
        • Tüm FOR seçenekleri aşama 2'de tamamen ayrıştırılır.
        • IN yan tümcesi davranır parantez <LF>olarak <space>. IN deyimi ayrıştırıldıktan sonra, tüm belirteçler tek bir belirteç oluşturmak için bir araya getirilir.
        • Ardışık çıkışsız / sıralanmamış simge sınırlayıcıları, DO aracılığıyla FOR komutu boyunca tek bir boşluğa daraltır.
      • DO'dan sonraki bölüm, normal olarak ayrıştırılan bir komut bloğudur. Daha sonra DO komut bloğunun işlenmesi, faz 7'deki yineleme ile kontrol edilir.
    • Faz 2'de tespit edilen REM, diğer tüm komutlardan önemli ölçüde farklı muamele görür.
      • Yalnızca bir bağımsız değişken belirteci ayrıştırılır - ayrıştırıcı, ilk bağımsız değişken belirtecinden sonraki karakterleri yok sayar.
      • REM komutu, faz 3 çıkışında görünebilir, ancak komut hiçbir zaman yürütülmez ve orijinal bağımsız değişken metni yankılanır - kaçan satırlar kaldırılmaz, ancak ...
        • ^Satırı sona erdiren çıkış karakterini biten tek bir argüman belirteci varsa, argüman belirteci atılır ve sonraki satır ayrıştırılır ve REM'e eklenir. Birden fazla belirteç olana veya son karakter bulunmayana kadar bu tekrarlanır ^.
  • Komut belirteci ile başlıyorsa :ve bu, 2. aşamanın ilk turuysa (6. aşamadaki CALL nedeniyle yeniden başlatma değil)
    • Jeton normalde Beklenmedik Etiket olarak işlem görür .
      • Hattının geri kalan, ancak, ayrıştırılır ), <, >, &ve |artık özel bir anlama sahiptir. Satırın geri kalanının tamamı "komut" etiketinin bir parçası olarak kabul edilir.
      • Özel ^olmaya devam eder, yani satır devam etmesi etikete sonraki satırı eklemek için kullanılabilir.
      • Bir İcra Edilmemiş Etiket hemen bir komutun izlediği veya sürece bir parantez blok içinde ölümcül bir sözdizimi hata ile sonuçlanır Etiket Gerçekleştirilen sonraki satırda.
        • (artık Denetlenmeyen Etiketi izleyen ilk komut için özel bir anlamı yoktur .
      • Etiket ayrıştırma işlemi tamamlandıktan sonra komut iptal edilir. Etiket için sonraki aşamalar gerçekleşmez
    • Aşama 2'de bulunan bir etiketin , Aşama 7 boyunca ayrıştırmaya devam eden bir Yürütülmüş Etiket olarak ele alınmasına neden olabilecek üç istisna vardır .
      • İlerlettiği etiket belirteci olduğunu yönlendirme yoktur ve orada |boru veya &, &&ya da ||on line komut birleştirme.
      • Etiket belirtecinden önce gelen yönlendirme vardır ve komut parantez içinde bir blok içerisindedir.
      • Etiket belirteci, parantez içindeki bir satırdaki bir satırdaki ilk komuttur ve yukarıdaki satır Beklenmedik Etiket ile sona erdi .
    • Faz 2'de bir Yürütülmüş Etiket bulunduğunda aşağıdakiler gerçekleşir
      • Etiket, argümanları ve yönlendirmesi, 3. aşamadaki herhangi bir yankı çıktısının dışında bırakılır
      • Satırdaki sonraki birleştirilmiş komutlar tamamen ayrıştırılır ve yürütülür.
    • Yürütülen Etiketler ve Beklenmeyen Etiketler hakkında daha fazla bilgi için bkz. Https://www.dostips.com/forum/viewtopic.php?f=3&t=3803&p=55405#p55405

Aşama 3) Ayrıştırılan komutları yankılama Yalnızca komut bloğu başlamamışsa @ve önceki adımın başında ECHO AÇIK ise.

Aşama 4) FOR %Xdeğişken genişletme: Sadece bir FOR komutu aktifse ve DO'dan sonraki komutlar işleniyorsa.

  • Bu noktada, toplu işlemin 1. aşaması zaten bir FOR değişkenini benzer hale %%Xgetirmiş olacaktır %X. Komut satırı, 1. aşama için farklı yüzde genişletme kurallarına sahiptir. Bu, komut satırlarının kullanması, %Xancak toplu iş dosyaları %%XFOR değişkenleri için kullanmasının nedenidir .
  • FOR değişken adları büyük / küçük harfe duyarlıdır, ancak ~modifiersbüyük / küçük harfe duyarlı değildir.
  • ~modifiersDeğişken isimlerine göre öncelik kazanır. Aşağıdaki karakter ~hem bir değiştirici hem de geçerli bir FOR değişkeni adı ise ve etkin bir FOR değişkeni adı olan bir sonraki karakter varsa, karakter değiştirici olarak yorumlanır.
  • FOR değişken adları geneldir, ancak yalnızca bir DO cümlesi bağlamındadır. Bir yordam bir FOR DO yantümcesinden çağırılırsa, FOR değişkenleri ARANAN yordamın içinde genişletilmez. Ancak rutinin kendi FOR komutu varsa, o anda tanımlanmış olan tüm FOR değişkenlerine iç DO komutları tarafından erişilebilir.
  • FOR değişken adları iç içe FORs içinde yeniden kullanılabilir. İç FOR değeri öncelik taşır, ancak INNER FOR kapatıldıktan sonra dış FOR değeri geri yüklenir.
  • ECHO bu aşamanın başlangıcında AÇIK ise, FOR 3 değişkenleri genişletildikten sonra ayrıştırılmış DO komutlarını göstermek için faz 3) tekrarlanır.

---- Bu noktadan itibaren, 2. aşamada tanımlanan her komut ayrı ayrı işlenir.
---- Bir komut için diğerine geçmeden önce 5 ila 7 arasındaki aşamalar tamamlanır.

Aşama 5) Gecikmeli Genişleme: Yalnızca gecikmeli genişleme açıksa, komut bir borunun her iki tarafındaki parantez içinde yer almaz ve komut "açık" bir toplu komut dosyası değildir (parantez olmadan komut dosyası adı, CALL, komut birleştirme, veya boru).

  • Bir komutun her belirteci, gecikmeli genişletme için bağımsız olarak ayrıştırılır.
    • Çoğu komut iki veya daha fazla jetonu ayrıştırır - komut jetonu, bağımsız değişken jetonu ve her yeniden yönlendirme hedef jetonu.
    • FOR komutu yalnızca IN yan tümcesi belirtecini ayrıştırır.
    • IF komutu karşılaştırma değerlerini yalnızca karşılaştırır - karşılaştırma işlecine bağlı olarak bir veya iki.
  • Ayrıştırılan her jeton için öncelikle içerdiği herhangi bir simge olup olmadığını kontrol edin !. Değilse, kod ayrıştırılmaz - ^karakterler için önemlidir . Jeton içeriyorsa !, her karakteri soldan sağa tarayın:
    • Eğer bir düzeltme işareti ( ^) ise, bir sonraki karakterin özel bir anlamı yoktur, düzeltme işaretinin kendisi kaldırılır
    • Bir ünlem işareti ise, bir sonraki ünlem işaretini arayın (artık düzeltme işaretleri görülmez), değişkenin değerine genişletin.
      • Art arda açılan !tek bir parça halinde!
      • Eşleştirilmemiş kalanlar !kaldırıldı
    • Bu aşamada genişleyen değişkenler "güvenli" dir, çünkü artık özel karakterler algılanmaz (hatta <CR>veya <LF>)
    • Daha eksiksiz bir açıklama için, dbenham aynı iplikten bunun 2. yarısını okuyun - Exclamation Point Phase

Aşama 5.3) Boru işleme: Yalnızca komutlar bir borunun
her iki tarafındaysa Borunun her iki tarafı bağımsız ve asenkron olarak işlenir.

  • Komut cmd.exe dosyasının içindeyse veya bir toplu iş dosyasıysa veya parantez içine alınmış bir komut bloğuysa, yeni bir cmd.exe iş parçacığında yürütülür %comspec% /S /D /c" commandBlock", bu nedenle komut bloğu bir faz yeniden başlatma alır, ancak bu sefer komut satırı modunda.
    • Parantez içine alınmış bir komut bloğu varsa, <LF>önce ve sonra bir komuta sahip olanların tümü dönüştürülür <space>&. Diğerleri <LF>soyulur.
  • Bu, boru komutları için işlemenin sonu.
  • Bkz zaman bir kod borulu bloğunun içinde Neden gecikmeli genişleme başarısız oluyor? boru ayrıştırma ve işleme hakkında daha fazla bilgi için

Aşama 5.5) Yeniden Yönlendirme Yürüt: 2. aşamada bulunan herhangi bir yeniden yönlendirme şimdi yürütülür.

Aşama 6) CALL işleme / Caret ikiye katlama: Yalnızca komut belirteci CALL ise veya ilk oluşan standart belirteç sınırlayıcıdan önceki metin CALL ise. CALL daha büyük bir komut belirtecinden ayrıştırılırsa, kullanılmayan kısım devam etmeden önce bağımsız değişken belirtecine eklenir.

  • Bağımsız değişken belirtecini tırnaksız olarak tarayın /?. Jetonlar içinde herhangi bir yerde bulunursa, 6. aşamayı iptal edin ve ÇAĞRI YARDIMININ yazdırılacağı Faz 7'ye geçin.
  • İlkini kaldırın CALL, böylece birden fazla ÇAĞRI istiflenebilir
  • Tüm taşıyıcıları ikiye katla
  • Faz 1, 1.5 ve 2'yi yeniden başlatın, ancak faz 3'e devam etmeyin
    • İki katına çıkarılan caretler, alıntılanmadığı sürece bir araca geri indirilir. Ancak maalesef alıntı yapılan caretler iki katına çıkar.
    • Faz 1 biraz değişiyor
      • Adım 1.2 veya 1.3'teki genişletme hataları CALL'u iptal eder, ancak hata ölümcül değildir - toplu işleme devam eder.
    • Faz 2 görevleri biraz değiştirildi
      • Aşama 2'nin ilk turunda algılanmayan yeni görünen tırnaksız, çıkışsız yeniden yönlendirme algılanır, ancak yeniden yönlendirme gerçekleştirilmeden kaldırılır (dosya adı dahil)
      • Hattın sonunda yeni görünen alıntılanmamış, boş bırakılmayan çizgi, çizgi devam etmeden kaldırılır
      • Aşağıdakilerden herhangi biri algılanırsa ÇAĞRI hatasız iptal edilir
        • Yeni görünen alıntılanmamış, boş bırakılmayan &veya|
        • Ortaya çıkan komut belirteci alıntılanmamış, çıkışsız ile başlar (
        • Kaldırılan CALL'ın başlamasından sonraki ilk belirteç @
      • Ortaya çıkan komut, görünüşte geçerli bir IF veya FOR ise, yürütme daha sonra bunu belirten IFveya FORdahili veya harici komut olarak tanınmayan bir hata ile başarısız olur .
      • Sonuçta elde edilen komut belirteci ile başlayan bir etiketse, elbette CALL, 2. fazın bu 2. turunda iptal edilmez :.
  • Ortaya çıkan komut belirteci CALL ise, 6. Aşamayı yeniden başlatın (CALL kalmayana kadar tekrar eder)
  • Ortaya çıkan komut belirteci bir toplu iş komut dosyası veya: etiketiyse, ÇAĞRI'nın yürütülmesi, Aşama 6'nın geri kalanı tarafından tamamen işlenir.
    • ÇAĞRI tamamlandığında yürütmenin doğru konumdan devam edebilmesi için, çağrı yığınındaki geçerli toplu komut dosyası konumunu itin.
    • Tüm sonuç jetonlarını kullanarak CALL için% 0,% 1,% 2, ...% N ve% * bağımsız değişken belirteçlerini ayarlayın
    • Komut belirteci ile başlayan bir etiketse :,
      • Aşama 5'i yeniden başlatın. Bu, aşağıdakileri etkileyebilir: etiketin ARANMASI. Ancak% 0 vb. Belirteçleri önceden ayarlandığından, ÇAĞRI yordamına iletilen bağımsız değişkenleri değiştirmez.
      • Dosya işaretçisini altyordamın başlangıcına konumlandırmak için GOTO etiketini yürütün (aşağıdakileri izleyebilecek diğer simgeleri yoksayın) GOTO'nun nasıl çalıştığına ilişkin kurallar için Aşama 7'ye bakın.
        • : Label jetonu eksikse veya: label bulunamazsa, kaydedilen dosya konumunu geri yüklemek için çağrı yığını derhal açılır ve CALL iptal edilir.
        • : Etiketi /? İçeriyorsa,: etiketi aramak yerine GOTO yardımı yazdırılır. Dosya işaretçisi hareket etmiyor, öyle ki CALL'dan sonraki kod iki kez, bir kez CALL bağlamında ve sonra tekrar CALL dönüşünden sonra yürütülüyor. Bkz Neden ÇAĞRI bu senaryodaki GOTO Yardım iletisi? Ve neden komut bundan sonra iki kez yürütülür? daha fazla bilgi için.
    • Belirtilen toplu iş komut dosyasına başka aktarım denetimi.
    • CALLed: etiketinin veya komut dosyasının yürütülmesi EXIT / B veya dosya sonuna erişilene kadar devam eder; bu noktada CALL yığını açılır ve yürütme kaydedilen dosya konumundan devam eder.
      ARAMA komut dosyaları veya: etiketleri için Aşama 7 yürütülmez.
  • Aksi takdirde, faz 6'nın sonucu yürütme için faz 7'ye düşer.

Aşama 7) Yürüt: Komut yürütülür

  • 7.1 - Dahili komutu yürüt - Komut jetonu belirtilmişse, bu adımı atlayın. Aksi takdirde, dahili bir komutu çözümlemeye ve yürütmeye çalışın.
    • Alıntılanmamış bir komut belirtecinin bir iç komutu temsil edip etmediğini belirlemek için aşağıdaki testler yapılır:
      • Komut belirteci dahili bir komutla tam olarak eşleşiyorsa, yürütün.
      • Komut belirtecinin ilk oluşumundan önce kırılması + / [ ] <space> <tab> , ;veya =
        Önceki metin dahili bir komutsa, o komutu hatırlayın
        • Komut satırı modundaysa veya komut parantez içine alınmış bir bloksa, IF doğru veya yanlış komut bloğu, FOR DO komut bloğu ise veya komut birleştirme işlemiyle ilgiliyse, dahili komutu yürütün
        • Başka (toplu modda tek başına bir komut olmalıdır) geçerli klasörü ve PATH'yi, taban adı orijinal komut belirteciyle eşleşen bir .COM, .EXE, .BAT veya .CMD dosyası için tarayın
          • İlk eşleşen dosya bir .BAT veya .CMD ise, 7.3.exec dosyasına gidin ve bu komut dosyasını yürütün
          • Başka (eşleşme bulunamadı veya ilk eşleşme .EXE veya .COM) hatırlanan dahili komutu yürütün
      • Else komut belirtecini ilk oluşumundan önce kırma . \veya :
        Önceki metin dahili bir komut değilse, 7.2'ye git
        Önceki metin başka bir dahili komut olabilir. Bu komutu hatırla.
      • Komut belirtecini ilk oluşumundan önce kırma + / [ ] <space> <tab> , ;veya =
        Önceki metin varolan bir dosyaya giden yolsa, 7,2
        Diğerse hatırlanan dahili komutu yürütür.
    • Bir iç komut daha büyük bir komut belirtecinden ayrıştırılırsa, komut belirtecinin kullanılmayan kısmı bağımsız değişken listesine eklenir
    • Bir komut belirtecinin dahili bir komut olarak ayrıştırılması, başarılı bir şekilde yürütülmesi anlamına gelmez. Her dahili komutun, bağımsız değişkenlerin ve seçeneklerin nasıl ayrıştırıldığı ve hangi sözdizimine izin verildiğiyle ilgili kendi kuralları vardır.
    • Tüm dahili komutlar /?algılanırsa işlevlerini yerine yardım yazdırır . Çoğu /?, argümanlarda herhangi bir yerde göründüğünü tanır . Ancak ECHO ve SET gibi birkaç komut yalnızca ilk argüman belirteci ile başlarsa yardımı yazdırır /?.
    • SET'in bazı ilginç semantiği vardır:
      • SET komutunun değişken adı ve uzantıları etkinleştirilmeden önce bir alıntısı varsa
        set "name=content" ignored -> değer = content
        o zaman ilk eşittir işareti ile son tırnak arasındaki metin içerik olarak kullanılır (ilk eşit ve son tırnak hariç). Son alıntıdan sonraki metin yoksayılır. Eşittir işaretinden sonra bir alıntı yoksa, satırın geri kalanı içerik olarak kullanılır.
      • SET komutunun
        set name="content" not ignored -> value = adından önce bir alıntı yoksa "content" not ignored
        , eşitten sonraki satırın geri kalanının tamamı, mevcut olabilecek tüm tırnak işaretleri de dahil olmak üzere içerik olarak kullanılır.
    • Bir IF karşılaştırması değerlendirilir ve koşulun doğru veya yanlış olmasına bağlı olarak, 5. aşamadan başlayarak uygun olarak ayrıştırılmış bağımlı komut bloğu işlenir.
    • FOR komutunun IN yan tümcesi uygun şekilde yinelenir.
      • Bu bir komut bloğunun çıkışını yineleyen bir FOR / F ise, o zaman:
        • IN yan tümcesi CMD / C aracılığıyla yeni bir cmd.exe işleminde yürütülür.
        • Komut bloğu tüm ayrıştırma işleminden ikinci kez geçmelidir, ancak bu sefer bir komut satırı bağlamında
        • ECHO başlayacak ve gecikmeli genişletme genellikle devre dışı bırakılacaktır (kayıt defteri ayarına bağlı olarak)
        • Alt cmd.exe işlemi sona erdiğinde IN yan tümcesi komut bloğu tarafından yapılan tüm ortam değişiklikleri kaybolur
      • Her yineleme için:
        • FOR değişken değerleri tanımlanır
        • Daha önce ayrıştırılmış DO komut bloğu daha sonra 4. aşamadan başlayarak işlenir.
    • GOTO, etiketi bulmak için aşağıdaki mantığı kullanır:
      • Etiket ilk bağımsız değişken belirtecinden ayrıştırılır
      • Komut dosyası, etiketin bir sonraki tekrarlaması için taranır
        • Tarama geçerli dosya konumundan başlar
        • Dosyanın sonuna erişilirse, tarama dosyanın başına geri döner ve orijinal başlangıç ​​noktasına devam eder.
      • Tarama, bulduğu etiketin ilk gerçekleşmesinde durur ve dosya işaretçisi, etiketi hemen takip eden satıra ayarlanır. Komut dosyasının yürütülmesi bu noktadan itibaren devam eder. Başarılı bir gerçek GOTO'nun, FOR döngüleri de dahil olmak üzere ayrıştırılmış kod bloğunu derhal iptal edeceğini unutmayın.
      • Etiket bulunamazsa veya etiket belirteci eksikse, GOTO başarısız olur, bir hata mesajı yazdırılır ve çağrı yığını açılır. Bu, etkili bir şekilde EXIT / B olarak işlev görür, ancak geçerli komut bloğunda GOTO'yu izleyen ayrıştırılmış komutlar hala yürütülür, ancak CALLer bağlamında (EXIT / B'den sonra var olan bağlam)
      • Etiketleri ayrıştırmak için kullanılan kuralların daha kesin bir açıklaması için bkz. Https://www.dostips.com/forum/viewtopic.php?f=3&t=3803 .
    • RENAME ve COPY, kaynak ve hedef yollar için joker karakter kabul eder. Ancak Microsoft, özellikle hedef yol için joker karakterlerin nasıl çalıştığını belgeleyen korkunç bir iş çıkarır. Windows RENAME komutu joker karakterleri nasıl yorumluyor? Bölümünde yararlı bir joker karakter kuralları dizisi bulunabilir.
  • 7.2 - Ses düzeyi değiştirme yürütme - Komut belirteci bir alıntı ile başlamazsa, tam olarak iki karakter uzunluğunda ve 2. karakter iki nokta üst üste ise, ses düzeyini değiştirin
    • Tüm argüman simgeleri yok sayılır
    • İlk karakter tarafından belirtilen birim bulunamazsa, hatayla iptal edin
    • İçin ::bir birim tanımlamak için SUBST kullanılmadığı sürece, komut jetonu her zaman hataya neden olur. Bir birim tanımlamak için ::
      SUBST kullanılırsa ::, birim değiştirilir, etiket olarak değerlendirilmez.
  • 7.3 - Harici komut yürütme - Aksi takdirde komutu harici bir komut olarak ele almaya çalışın.
    • Komut satırı modu ve komut işlem görmeyen ve bir hacim özellikleri, beyaz boşluk ile başlamıyorsa, ,, ;, =ya da +daha sonra ilk durumda belirteci komutu kırmak <space> , ;ya da =ve (ler) belirteci argüman kalan çizgi yerleştirirler.
    • Komut simgesinin 2. karakteri iki nokta üst üste ise, 1. karakter tarafından belirtilen ses düzeyinin bulunabildiğini doğrulayın.
      Birim bulunamıyorsa, hatayla iptal edin.
    • Toplu modda ve komut belirteci ile başlarsa, :git 7.4 7.4
      Etiket belirteci ile başlarsa, ::bir birim tanımlamak için SUBST kullanılmadıkça önceki adımda bir hata ile iptal edileceğinden buna ulaşılamayacağını unutmayın ::.
    • Yürütülecek harici komutu tanımlayın.
      • Bu, geçerli birim, geçerli dizin, PATH değişkeni, PATHEXT değişkeni ve veya dosya ilişkilendirmelerini içerebilen karmaşık bir işlemdir.
      • Geçerli bir harici komut tanımlanamazsa, hatayla iptal edin.
    • Komut satırı modunda ve komut belirteci ile başlıyorsa :, 7.4'e gidin
      . Komut belirteci ile başlamadığı ::ve SUBST için bir birim tanımlanmadığı sürece bir önceki adımda bir hata ile iptal edildiğinden ::ve tüm komut simgesi harici bir komut için geçerli bir yoldur.
    • 7.3.exec - Harici komutu yürütün.
  • 7.4 - Etiketi yoksay - Komut simgesi ile başlarsa komutu ve tüm bağımsız değişkenlerini yok sayın :.
    7.2 ve 7.3'deki kurallar bir etiketin bu noktaya ulaşmasını engelleyebilir.

Komut Satırı Ayrıştırıcı:

BatchLine-Parser gibi çalışır, ancak:

Aşama 1) Yüzde Genişleme:

  • Hayır %*, %1vb. Argüman genişletme
  • Var tanımsızsa, %var%değişmeden kalır.
  • Özel işlem yok %%. Var = content ise, %%var%%değerine genişler %content%.

Aşama 3) Ayrıştırılan komutları yankılayın

  • Bu, 2. aşamadan sonra gerçekleştirilmez. Sadece FOR DO komut bloğu için 4. aşamadan sonra gerçekleştirilir.

Aşama 5) Gecikmeli Genişleme: yalnızca Gecikmeli Genişletme etkinse

  • Var tanımsızsa, !var!değişmeden kalır.

Aşama 7) Komutu Yürüt

  • CALL veya GOTO a: label girişimleri hatayla sonuçlanır.
  • Faz 7'de zaten belgelendiği gibi, yürütülen bir etiket farklı senaryolarda hataya neden olabilir.
    • Toplu olarak yürütülen etiketler, yalnızca şununla başlarlarsa hataya neden olabilir: ::
    • Komut satırı yürütülen etiketler neredeyse her zaman hataya neden olur

Tamsayı değerlerinin ayrıştırılması

Cmd.exe dosyasının dizelerden tamsayı değerlerini ayrıştırdığı ve kuralların tutarsız olduğu birçok farklı bağlam vardır:

  • SET /A
  • IF
  • %var:~n,m% (değişken alt dize genişletme)
  • FOR /F "TOKENS=n"
  • FOR /F "SKIP=n"
  • FOR /L %%A in (n1 n2 n3)
  • EXIT [/B] n

Bu kuralların ayrıntıları CMD.EXE'nin sayıları ayrıştırma kurallarında bulunabilir


Cmd.exe ayrıştırma kurallarını geliştirmek isteyen herkes için, DosTips forumunda sorunların bildirilebileceği ve önerilerin sunulduğu bir tartışma konusu vardır .

Umarım
Jan Erik (jeb) yardımcı olur - Orijinal yazar ve aşamaların keşfedicisi
Dave Benham (dbenham) - Çok fazla içerik ve düzenleme


4
Merhaba jeb, anlayışınız için teşekkür ederim… Anlamak zor olabilir, ama düşünmeye çalışacağım! Çok fazla test yapmış gibisin! Çeviri yaptığınız için teşekkür ederiz ( yönetici.de/… )
Benoit

2
Toplu aşama 5) - %% a, Faz 1'de zaten% a olarak değiştirilmiş olacak, bu nedenle döngü için genişletme gerçekten% a'yı genişletiyor. Ayrıca, aşağıdaki yanıta Batch phase 1 ile ilgili daha detaylı bir açıklama ekledim (düzenleme ayrıcalığım yok)
dbenham

3
Jeb - belki de faz 0 hareket ettirilebilir ve faz 6 ile birleştirilebilir mi? Bu benim için daha anlamlı, ya da böyle ayrılmalarının bir nedeni var mı?
dbenham

1
@aschipfl - Bu bölümü güncelledim. )Gerçekten neredeyse bir gibi işlev yapar REMparantez sayacı komut satırından Bunlardan ikisi 0. deneyin olduğunda komuta: ) Ignore thisveecho OK & ) Ignore this
dbenham

1
@aschipfl evet bu doğru, bu nedenle bazen 'set "var =% expr%"! 'son ünlem işareti kaldırılacak ancak faz 5'i
zorlayacak

62

Komut penceresinden bir komut çağrılırken, komut satırı argümanlarının simgesi cmd.exe(aka "kabuk" olarak) yapılmaz . Çoğu zaman tokenizasyon yeni oluşturulan süreçlerin C / C ++ çalışma zamanı tarafından yapılır, ancak bu mutlaka böyle değildir - örneğin, yeni işlem C / C ++ 'da yazılmadıysa veya yeni süreç yoksaymayı argvve işlemeyi seçerse kendisi için ham komut satırı (ör. GetCommandLine () ile). İşletim sistemi düzeyinde, Windows yeni işlemlere tek bir dize olarak işaretsiz komut satırlarını iletir. Bu, kabuğun argümanları yeni oluşturulan sürece geçirmeden önce tutarlı ve öngörülebilir bir şekilde tokenleştirdiği çoğu * nix merminin aksine. Tüm bunlar, bireysel programlar genellikle kendi argüman belirteçlerini kendi ellerine aldığından, Windows'ta farklı programlarda çılgınca farklı argüman belirteç davranışları yaşayabileceğiniz anlamına gelir.

Anarşi gibi geliyorsa, öyle. Ancak, Windows programlarının çok sayıda beri do Microsoft C / C ++ çalışma zamanı en yararlanmak argv, bunu anlamak genellikle yararlı olabilir MSVCRT sıfırlar nasıl argümanlar. İşte bir alıntı:

  • Bağımsız değişkenler boşluk veya sekme olan beyaz boşlukla sınırlandırılır.
  • Çift tırnak işaretleriyle çevrelenen bir dize, içindeki boşluktan bağımsız olarak tek bir argüman olarak yorumlanır. Alıntılanan bir dize bir bağımsız değişkene gömülebilir. Düzeltme işareti (^) bir kaçış karakteri veya sınırlayıcı olarak tanınmadı.
  • Bir ters eğik çizgiden önce gelen çift tırnak işareti \ ", gerçek bir çift tırnak işareti (") olarak yorumlanır.
  • Ters eğik çizgiler, hemen bir çift tırnak işaretinden önce olmadıkça, tam anlamıyla yorumlanır.
  • Çift sayıda ters eğik çizgiyi çift tırnak işareti izliyorsa, her ters eğik çizgi çifti (\) için argv dizisine bir ters eğik çizgi () yerleştirilir ve çift tırnak işareti (") bir dize sınırlayıcı olarak yorumlanır.
  • Tek sayıda ters eğik çizgiyi çift tırnak işareti izliyorsa, her ters eğik çizgi (\) çifti için argv dizisine bir ters eğik çizgi () yerleştirilir ve çift tırnak işareti kalan ters eğik çizgi tarafından bir kaçış dizisi olarak yorumlanır. argv'ye yerleştirilecek gerçek bir çift tırnak işareti (").

Microsoft "toplu dil" ( .bat) bu anarşik ortama bir istisna değildir ve tokenizasyon ve kaçış için kendi benzersiz kurallarını geliştirmiştir. Ayrıca cmd.exe'nin komut istemi, argümanı yeni yürütme işlemine geçirmeden önce komut satırı argümanının bazı ön işlemlerini (çoğunlukla değişken değiştirme ve çıkış için) yapar gibi görünüyor. Bu sayfada jeb ve dbenham'ın mükemmel cevaplarında toplu dil ve cmd'nin düşük seviyeli ayrıntıları hakkında daha fazla bilgi bulabilirsiniz.


C'de basit bir komut satırı yardımcı programı oluşturalım ve test durumlarınız hakkında ne söylediğine bakalım:

int main(int argc, char* argv[]) {
    int i;
    for (i = 0; i < argc; i++) {
        printf("argv[%d][%s]\n", i, argv[i]);
    }
    return 0;
}

(Notlar: argv [0] her zaman yürütülebilir dosyanın adıdır ve kısalık için aşağıda atlanmıştır. Windows XP SP3'te test edilmiştir. Visual Studio 2005 ile derlenmiştir.)

> test.exe "a ""b"" c"
argv[1][a "b" c]

> test.exe """a b c"""
argv[1]["a b c"]

> test.exe "a"" b c
argv[1][a" b c]

Ve kendi testlerimden birkaçı:

> test.exe a "b" c
argv[1][a]
argv[2][b]
argv[3][c]

> test.exe a "b c" "d e
argv[1][a]
argv[2][b c]
argv[3][d e]

> test.exe a \"b\" c
argv[1][a]
argv[2]["b"]
argv[3][c]

Cevabınız için teşekkür ederim. TinyPerl'in programınızın çıktısını vermeyeceğini görmek için daha da fazla bulmaca yapıyor ve post-processing nasıl [a "b" c]olabileceğini anlamakta zorlanıyorum [a "b] [c].
Benoit

Şimdi düşündüğüme göre, komut satırının bu simgesi muhtemelen tamamen C çalışma zamanı tarafından yapılır. Bir yürütülebilir dosya, C çalışma zamanını bile kullanamayacak şekilde yazılabilir, bu durumda komut satırı kelimesi kelimesiyle başa çıkmak zorunda kalacağını ve kendi jetonizasyonunu yapmaktan sorumlu olacaktı (eğer istiyorsa). uygulamanız C çalışma zamanını kullanıyorsa, argc ve argv'yi yok saymayı ve yalnızca komut satırını örneğin Win32 aracılığıyla almayı seçebilirsiniz GetCommandLine. Belki TinyPerl argv'yi görmezden gelir ve ham komut satırını kendi kurallarına göre tokenize eder.
Mike Clark

4
"Win32'nin bakış açısından, komut satırının sadece yeni işlemin adres alanına kopyalanan bir dize olduğunu unutmayın. Başlatma işlemi ve yeni işlemin bu dizeyi nasıl yorumladığı kurallara göre değil, kurallara göre yönetilir." -Raymond Chen blogs.msdn.com/b/oldnewthing/archive/2009/11/25/9928372.aspx
Mike Clark

2
Bu güzel cevap için teşekkürler. Bu bence çok şey açıklıyor. Ve bu da bazen neden Windows ile çalışmak için gerçekten boktan bulduğumu açıklıyor…
Benoit

Bulduğum bu Win32 C ++ programları için, komut satırında argv en dönüşümde sırasında ilgili ters eğik çizgi ve tırnak. Ters eğik çizgi sayısı yalnızca son ters eğik çizgiyi bir dblquote izlediğinde ikiye bölünür ve dblquote, daha önce çift sayıda ters eğik çizgi olduğunda bir dizeyi sonlandırır.
Benoit

47

Yüzde Genişleme Kuralları

Burada, jeb yanıtında Faz 1'in genişletilmiş açıklaması (hem toplu iş modu hem de komut satırı modu için geçerlidir).

Aşama 1) Genişleme Yüzdesi Soldan başlayarak, her karakteri %veya için tarayın <LF>. Eğer bulunursa

  • 1,05 (kesik çizgi <LF>)
    • Karakter o <LF>zaman
      • Çizginin geri kalanını başından <LF>itibaren bırakın (yoksayın)
      • Git Aşama 1.5 (Şerit <CR>)
    • Başka karakter olmalıdır %, bu yüzden 1.1'e ilerleyin
  • 1.1 (çıkış %) komut satırı modu atlanırsa
    • Toplu modunda ise ve başka izledi %sonra
      değiştirin %%tek olan %ve tarama devam
  • Komut satırı modu 1.2 (bağımsız değişkeni genişlet) atlandı
    • Başka bir toplu iş modu varsa
      • Ardından ise *ve komut uzantıları sonra etkindir
        değiştirin %*tüm komut satırı bağımsız değişkenleri metin ile (hiçbir argüman varsa hiçbir şey ile değiştirin) ve tarama devam edin.
      • Ardından eğer Else <digit>sonra
        değiştirin %<digit>argüman değeri ile (tanımsız ise hiçbir şey ile değiştirin) ve tarama devam ediyor.
      • Bunu izleyen başka bir ~komut varsa ve komut uzantıları etkinleştirildiyse
        • Gerekli ardından argüman değiştiricilerinin opsiyonel geçerli listesi izler varsa <digit>o zaman
          değiştirin %~[modifiers]<digit>ve tarama devam: Modifiye argüman değeri ile (değiştirici tanımlanmadı tanımlanmamış veya $ PATH belirtilmişse eğer hiçbir şey ile değiştirin).
          Not: değiştiriciler büyük / küçük harfe duyarsızdır ve $ PATH: modifier yalnızca bir kez görünebilir ve en son değiştirici olmalıdır.<digit>
        • Else geçersiz değiştirilmiş argüman sözdizimi ölümcül hataya yol açar: Tüm ayrıştırılmış komutlar iptal edilir ve toplu iş modundaysa toplu işlem iptal edilir!
  • 1.3 (genişletme değişkeni)
    • Komut uzantıları devre dışı bırakılmışsa , arabellekten
      önce %veya sondan sonra bir sonraki karakter dizisine bakın ve bunları VAR olarak adlandırın (boş bir liste olabilir)
      • Bir sonraki karakteri ise %o zaman
        • VAR tanımlanmışsa , VAR değeri ile
          değiştir %VAR%ve taramaya devam et
        • Başka bir toplu iş modu varsa
          Kaldır %VAR%ve taramaya devam et
        • Diğer 1.4
      • Diğer 1.4
    • Komut uzantıları etkinse , arabellekten
      önce % :veya sondan sonra bir sonraki karakter dizisine bakın ve bunları VAR olarak adlandırın (boş bir liste olabilir). VAR önce kırılırsa :ve sonraki karakter VAR'daki son karakter olarak %dahil edilirse :ve önce kırılırsa %.
      • Bir sonraki karakteri ise %o zaman
        • VAR tanımlanmışsa , VAR değeri ile
          değiştir %VAR%ve taramaya devam et
        • Başka bir toplu iş modu varsa
          Kaldır %VAR%ve taramaya devam et
        • Diğer 1.4
      • Bir sonraki karakter ise Else :sonra
        • VAR tanımsızsa
          • Toplu mod ise,
            Kaldır %VAR:ve taramaya devam et.
          • Diğer 1.4
        • Bir sonraki karakter ise Else ~sonra
          • Karakterlerin sonraki dize modelini eşleşirse [integer][,[integer]]%sonra
            değiştirin %VAR:~[integer][,[integer]]%(muhtemelen boş dize ile sonuçlanan) ve tarama devam VAR değerinin alt dize ile.
          • Diğer 1.4
        • Ardından =veya *=sonra
          geçersiz değişken arama ve değiştirme sözdizimi ölümcül hata oluşturur: Ayrıştırılan tüm komutlar iptal edilir ve toplu iş modundayken toplu işlem iptal edilir!
        • Karakterlerin sonraki dize modelini maçları else if [*]search=[replace]%arama hariç herhangi bir karakter grubuyla içerebilir, =ve benzerleri hariç herhangi bir karakter grubuyla içerebilir yerine %, sonra
          değiştirin %VAR:[*]search=[replace]%(muhtemelen boş dize ile sonuçlanan) arama yaptıktan sonra VAR değeriyle ve değiştirin ve devam taramak
        • Diğer 1.4
  • 1.4 (şerit%)
    • Başka Toplu mod ise,
      Kaldır %ve sonraki karakterden sonra taramaya devam et.%
    • Diğer satır başını koru ve korunan %satır başından sonraki karakterle başlayarak taramaya devam et%

Yukarıdakiler bu partinin neden açıklanmasına yardımcı olur

@echo off
setlocal enableDelayedExpansion
set "1var=varA"
set "~f1var=varB"
call :test "arg1"
exit /b  
::
:test "arg1"
echo %%1var%% = %1var%
echo ^^^!1var^^^! = !1var!
echo --------
echo %%~f1var%% = %~f1var%
echo ^^^!~f1var^^^! = !~f1var!
exit /b

Bu sonuçları verir:

%1var% = "arg1"var
!1var! = varA
--------
%~f1var% = P:\arg1var
!~f1var! = varB

Not 1 - Faz 1, REM ifadelerinin tanınmasından önce gerçekleşir. Bu çok önemlidir, çünkü geçersiz argüman genişletme sözdizimi veya geçersiz değişken arama ve değiştirme sözdizimi varsa bir yorum bile ölümcül bir hata oluşturabilir!

@echo off
rem %~x This generates a fatal argument expansion error
echo this line is never reached

Not 2 -% ayrıştırma kurallarının bir başka ilginç sonucu: Adında: içeren değişkenler tanımlanabilir, ancak komut uzantıları devre dışı bırakılmadığı sürece genişletilemez. Bir istisna vardır - komut uzantıları etkinleştirilirken sonunda tek bir iki nokta içeren değişken adı genişletilebilir. Ancak, iki nokta üst üste ile biten değişken adlarında alt dize gerçekleştiremez veya arama ve değiştirme işlemleri gerçekleştiremezsiniz. Aşağıdaki toplu iş dosyası (jeb'in izniyle) bu davranışı gösterir

@echo off
setlocal
set var=content
set var:=Special
set var::=double colon
set var:~0,2=tricky
set var::~0,2=unfortunate
echo %var%
echo %var:%
echo %var::%
echo %var:~0,2%
echo %var::~0,2%
echo Now with DisableExtensions
setlocal DisableExtensions
echo %var%
echo %var:%
echo %var::%
echo %var:~0,2%
echo %var::~0,2%

Not 3 - Jeb'in gönderisinde yer alan ayrıştırma kurallarının sırasının ilginç bir sonucu: Gecikmeli genişletme ile bul ve değiştir gerçekleştirirken, hem bul hem de değiştir terimlerindeki özel karakterlerden kaçınılmalıdır. Ancak durum, genişleme yüzdesi için farklıdır - bulma teriminden kaçınılmamalıdır (ancak alıntılanabilir). Değişim yüzdesi dizesi, amacınıza bağlı olarak kaçış veya alıntı gerektirebilir veya gerektirmeyebilir.

@echo off
setlocal enableDelayedExpansion
set "var=this & that"
echo %var:&=and%
echo "%var:&=and%"
echo !var:^&=and!
echo "!var:&=and!"

Gecikmeli Genişleme Kuralları

Burada, jeb cevabında faz 5'in genişletilmiş ve daha doğru bir açıklaması (Hem toplu iş modu hem de komut satırı modu için geçerlidir)

Aşama 5) Gecikmeli Genişleme

Aşağıdaki koşullardan biri geçerli olduğunda bu aşama atlanır:

  • Gecikmeli genişletme devre dışı.
  • Komut, bir borunun her iki tarafındaki parantez içinde bir blok içerisindedir.
  • Gelen komut belirteci o ile ilişkili değildir, yani bir "çıplak" toplu komut dosyası CALL, parantez blok, komut birleştirme herhangi bir formu ( &, &&ya da ||) veya bir boru |.

Gecikmeli genişleme işlemi jetonlara bağımsız olarak uygulanır. Bir komutun birden fazla jetonu olabilir:

  • Komut belirteci. Çoğu komut için komut adının kendisi bir belirteçtir. Ancak bazı komutlar, 5. aşama için TOKEN olarak kabul edilen özel bölgelere sahiptir.
    • for ... in(TOKEN) do
    • if defined TOKEN
    • if exists TOKEN
    • if errorlevel TOKEN
    • if cmdextversion TOKEN
    • if TOKEN comparison TOKENKarşılaştırma biri olduğu ==, equ, neq, lss, leq, gtr, ya dageq
  • Bağımsız değişken simgesi
  • Yönlendirmenin hedef belirteci (yönlendirme başına bir)

İçermeyen jetonlarda değişiklik yapılmaz !.

En az bir tane içeren her belirteç için, !her karakteri soldan sağa ^veya için tarayın !ve bulunursa, o zaman

  • 5.1 (düzeltme işareti kaçış) Gerekli !veya ^hazır değerler
    • Karakteri bir şapka ise ^o zaman
      • Kaldır ^
      • Sonraki karakteri tara ve değişmez olarak sakla
      • Taramaya devam et
  • 5.2 (değişkeni genişlet)
    • Karakter ise !, o zaman
      • Komut uzantıları devre dışı bırakılmışsa
        , bir sonraki karakter dizisine bakın !veya önce <LF>kırın ve VAR olarak adlandırın (boş bir liste olabilir)
        • Bir sonraki karakteri ise !o zaman
          • VAR tanımlanmışsa, VAR değeri ile
            değiştir !VAR!ve taramaya devam et
          • Başka bir toplu iş modu varsa
            Kaldır !VAR!ve taramaya devam et
          • Başka Git 5.2.1
        • Başka Git 5.2.1
      • Komut uzantıları etkinse Else sonra
        , karakterlerin sonraki dize bak önce kırılma !, :ya <LF>(boş bir liste olabilir), ve onları VAR diyoruz. VAR önce kırılırsa :ve sonraki karakter VAR'daki son karakter olarak !dahil :edilir ve önce kesilirse!
        • Bir sonraki karakteri ise !o zaman
          • VAR varsa, VAR değeri ile
            değiştir !VAR!ve taramaya devam et
          • Başka bir toplu iş modu varsa
            Kaldır !VAR!ve taramaya devam et
          • Başka Git 5.2.1
        • Bir sonraki karakter ise Else :sonra
          • VAR tanımsızsa
            • Toplu mod ise,
              Kaldır !VAR:ve taramaya devam et
            • Başka Git 5.2.1
          • Bir sonraki karakter ise Else ~sonra
            • Bir sonraki karakter dizisi deseni ile eşleşiyorsa , VAR değerinin alt dizesiyle [integer][,[integer]]!değiştirin !VAR:~[integer][,[integer]]!(muhtemelen boş dizeyle sonuçlanır) ve taramaya devam edin.
            • Başka Git 5.2.1
          • Karakterlerin sonraki dize modelini maçları else if [*]search=[replace]!arama hariç herhangi bir karakter grubuyla içerebilir, =ve benzerleri hariç herhangi bir karakter grubuyla içerebilir yerine !, sonra
            değiştirin !VAR:[*]search=[replace]!(muhtemelen boş bir dize ile sonuçlanan) arama yaptıktan sonra VAR değeriyle ve değiştirme ve taramaya devam et
          • Başka Git 5.2.1
        • Başka Git 5.2.1
      • 5.2.1
        • Toplu mod ise önde gelen !
          Else'yi kaldırın.!
        • Korunmuş satır aralığından sonraki karakterle başlayarak taramaya devam et !

3
+1, %definedVar:a=b%vs %undefinedVar:a=b%ve %var:~0x17,-010%formlar için burada yalnızca kolon sözdizimi ve kuralları eksik
jeb

2
İyi bir nokta - Endişelerinizi gidermek için değişken genişletme bölümünü genişlettim. Ayrıca, bazı eksik ayrıntıları doldurmak için argüman genişletme bölümünü genişlettim.
dbenham

2
Jeb'den bazı özel geri bildirimler aldıktan sonra, iki nokta üst üste ile biten değişken adları için bir kural ekledim ve not 2'yi ekledim. Ayrıca ilginç ve önemli olduğunu düşündüğüm için not 3'ü de ekledim.
dbenham

1
@aschipfl - Evet, bununla ilgili daha fazla ayrıntıya girmeyi düşündüm, ama o tavşan deliğinden aşağı inmek istemedim. Ben [integer] terimini kullandığımda kasıtlı olarak taahhüt olmayan oldu . CMD.EXE nasıl numaraları ayrıştırır için Kurallar hakkında daha fazla bilgi var .
dbenham

1
Değişken gibi adının ilk karakteri için hiçbir ayrılmış karakterler olmadığından emin gibi, cmd bağlam için genişleme kurallarını özlüyorum %<digit>, %*ya %~. Tanımlanmamış değişkenler için davranış değişir. Belki de ikinci bir cevap açmanız gerekir
jeb

7

Belirtildiği gibi, komutlar tüm argüman dizesini μSoft diyarında geçirilir ve bunu kendi kullanımları için ayrı argümanlara ayrıştırmak onlara bağlıdır. Bu konuda farklı programlar arasında herhangi bir tutarlılık yoktur ve bu nedenle bu süreci açıklayan tek bir kural kümesi yoktur. Programınızın kullandığı C kitaplığı için her köşe durumunu gerçekten kontrol etmeniz gerekir.

Sistem .batdosyalarına gelince , işte o test:

c> type args.cmd
@echo off
echo cmdcmdline:[%cmdcmdline%]
echo 0:[%0]
echo *:[%*]
set allargs=%*
if not defined allargs goto :eof
setlocal
@rem Wot about a nice for loop?
@rem Then we are in the land of delayedexpansion, !n!, call, etc.
@rem Plays havoc with args like %t%, a"b etc. ugh!
set n=1
:loop
    echo %n%:[%1]
    set /a n+=1
    shift
    set param=%1
    if defined param goto :loop
endlocal

Şimdi bazı testler yapabiliriz. ΜSoft'un ne yapmaya çalıştığını anlayabiliyor musunuz, bakın:

C>args a b c
cmdcmdline:[cmd.exe ]
0:[args]
*:[a b c]
1:[a]
2:[b]
3:[c]

Şimdiye kadar iyi. (İlgisiz %cmdcmdline%ve %0bundan sonra bırakacağım .)

C>args *.*
*:[*.*]
1:[*.*]

Dosya adı genişletmesi yok.

C>args "a b" c
*:["a b" c]
1:["a b"]
2:[c]

Tırnaklar argüman bölünmesini engellemesine rağmen, alıntı sıyırma yok.

c>args ""a b" c
*:[""a b" c]
1:[""a]
2:[b" c]

Ardışık çift tırnak, sahip oldukları özel ayrıştırma yeteneklerini kaybetmelerine neden olur. @ Beniot örneği:

C>args "a """ b "" c"""
*:["a """ b "" c"""]
1:["a """]
2:[b]
3:[""]
4:[c"""]

Test: Herhangi bir ortamın değerini tek bir argüman (yani, as %1) olarak bir bat dosyasına nasıl iletirsiniz ?

c>set t=a "b c
c>set t
t=a "b c
c>args %t%
1:[a]
2:["b c]
c>args "%t%"
1:["a "b]
2:[c"]
c>Aaaaaargh!

Sane ayrıştırma sonsuza dek kırılmış görünüyor.

Eğlence için, çeşitli eklemeyi deneyin ^, \, ', &bu örneklere (& c.) Karakterleri.


% T% değerini tek bir bağımsız değişken olarak iletmek için "% t:" = \ "%" kullanabilirsiniz. Yani, değişken genişletme için% VAR: str = replacement% sözdizimini kullanın. Kabuk metakarakterleri gibi | ve & değişken içeriği yine maruz ve tekrar kabuk kaçmasına rağmen kabuk berbat olabilir ....
Toughy

@Toughy Bu yüzden, benim örnekte tolduğu a "b c. O 6 karakter almak için bir reçete var mı ( a2 × uzay, ", b, ve c) olarak görünmesini %1bir iç .cmd? Düşüncelerinizi seviyorum. args "%t:"=""%"oldukça yakın :-)
bobbogo

5

Yukarıda bazı harika yanıtlarınız var, ancak sorunuzun bir kısmını yanıtlamak için:

set a =b, echo %a %b% c% → bb c%

Ne oluyor size önce bir boşluk var çünkü = bir değişken olarak adlandırılan oluşturulan olduğundan orada %a<space>% bunu yaparken echo %a %kadar doğru değerlendirildiği b.

Geri kalan kısım b% c%daha sonra düz metin + tanımlanmış bir değişken olarak değerlendirilir % c%, bu değişken benim gibi yazılmalı, benim için echo %a %b% c%geri dönerbb% c%

Değişken adlarına boşluk ekleyebilmenin planlanan bir 'özellik'ten daha fazla bir gözetim olduğundan şüpheleniyorum


0

edit: kabul edilen cevaba bakınız, takip edenler yanlıştır ve sadece bir komut satırının TinyPerl'e nasıl geçirileceğini açıklar.


Alıntılarla ilgili olarak, ben davranış aşağıdaki gibi hissediyorum:

  • a "bulunduğunda string globbing başlar
  • string globbing meydana geldiğinde:
    • a olmayan her karakter "globbed olur
    • a "bulunduğunda:
      • arkasından ""(böylece üçlü ") gelirse , dizeye bir çift tırnak eklenir
      • bunu "(böylece bir çift ") takip ederse , dize ve dize globbing uçlarına bir çift tırnak eklenir
      • sonraki karakter değilse ", dize globbing sona erer
    • çizgi bittiğinde, dize globbing sona erer.

Kısacası:

"a """ b "" c"""iki dizeden oluşur: a " b "vec"

"a"", "a"""Ve "a""""hepsi aynı dize olduğu bir satırın sonunda eğer


tokenizer ve string globbing komuta bağlıdır! Bir "küme" bir "çağrıdan" ve hatta bir "if" dan farklı çalışır
jeb

evet, ama harici komutlar ne olacak? Sanırım cmd.exe her zaman aynı argümanları onlara geçer?
Benoit

1
cmd.exe her zaman genişletme sonucunu, belirteçleri değil dize olarak harici bir komuta iletir. Dış komuttan nasıl
kaçacağını

0

Microsoft'un Terminal'in kaynak kodunu yayımladığını unutmayın. Sözdizimi ayrıştırma açısından komut satırına benzer şekilde çalışabilir. Belki biri terminalin ayrıştırma kurallarına göre tersine mühendislik çözümleme kurallarını test etmekle ilgilenir.

Bağlantı kaynak koduna.

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.