Bir dizeden keyfi bir yerel komutu nasıl yürütürsünüz?


208

İhtiyacımı aşağıdaki senaryo ile ifade edebilirim: Yerel bir komut olarak çalıştırılmak üzere bir dizeyi kabul eden bir işlev yazın.

Bir fikirden çok uzak değil: şirketin başka bir yerinden size başka bir komut satırı yardımcı programı ile arabirim oluşturuyorsanız, size kelimesi kelimesini çalıştırma komutu veren bir komut sağlayın. Komutu denetlemediğiniz için, geçerli herhangi bir komutu giriş olarak kabul etmeniz gerekir . Bunlar kolayca üstesinden gelemediğim ana hıçkırıklar:

  1. Komut, içinde boşluk bulunan bir yolda yaşayan bir programı yürütebilir:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
  2. Komutun içinde boşluk bulunan parametreler olabilir:

    $command = 'echo "hello world!"';
  3. Komutun hem tek hem de çift keneleri olabilir:

    $command = "echo `"it`'s`"";

Var mı herhangi Bu gerçekleştirmenin temiz şekilde? Sadece cömert ve çirkin geçici çözümler tasarlayabildim, ancak bir senaryo dili için bunun basit olması gerektiğini hissediyorum.

Yanıtlar:


318

Invoke-Expressionolarak da adlandırılır iex. Örnekler # 2 ve # 3 üzerinde çalışacaktır:

iex $command

Exe tırnak içinde olduğu için, örneğin # 1 örneğiniz gibi bazı dizeler olduğu gibi çalışmaz. Bu, olduğu gibi çalışacaktır, çünkü dizenin içeriği tam olarak doğrudan Powershell komut isteminden nasıl çalıştırılacağınızdır:

$command = 'C:\somepath\someexe.exe somearg'
iex $command

Ancak, exe tırnak &içinde ise, bu örnekte olduğu gibi, komut satırından çalıştırıldığı gibi çalıştırılması için yardıma ihtiyacınız vardır :

>> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"

Ve sonra senaryoda:

$command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
iex "& $command"

Muhtemelen, "bu naif uygulamadaki gibi komut dizesinin ilk karakterinin olup olmadığını tespit ederek neredeyse tüm vakaları işleyebilirsiniz :

function myeval($command) {
    if ($command[0] -eq '"') { iex "& $command" }
    else { iex $command }
}

Ancak, farklı bir şekilde çağrılması gereken başka durumlar da bulabilirsiniz. Bu durumda, try{}catch{}belki belirli istisna türleri / mesajları için kullanmanız veya komut dizesini incelemeniz gerekir.

Göreli yollar yerine her zaman mutlak yollar alırsanız, yukarıdaki 2'nin dışında çok sayıda özel durumunuz olmamalıdır.


3
Takma ad harika. Başka bir makineye geçerseniz veya bu komut dosyasını takma adın ayarlanmayacağını başkasına gönderirseniz unutmayın. PowerShell işlevlerinin tam adlarını tercih edin.
Doug Finke

1
@Doug: Çoğu zaman bunu yaparım veya yerleşik takma adları kullanırım (özellikle komut satırındaki kısalık için). evalŞeyler yarı şaka olduğunu o kadar çok diğer betik dillerinde denir ve bu kimse hakkında hiçbir fikri yoktu nerede gördüğüm ilk soru değil ne olduğunu yıllardan çünkü invoke-expression. Ve OP'nin davası sadece şirket içi senaryo gibi geliyor.
Joel B Fant

Bunu denedim, ancak ile çalışmıyor: $ command = '"C: \ Program Files \ Windows Media Player \ mplayer2.exe" "H: \ Ses \ Müzik \ Stevie Wonder \ Stevie Wonder - Superstition.mp3" '
Johnny Kauffman

3
Bir "&" veya "" koymanız gerekebilir. powershell-native değilse gerçek komuttan önce oturum açın, örneğinInvoke-Expression "& $command"
Torbjörn Bergstedt

Torbjörn Bergstedt kazandı! Büyülü ekstra "&" sorunumu çözdü! Sonuç olarak, hem mutlu hem de kızmışım.
Johnny Kauffman

19

Ayrıca, kabuk komutlarını (oh, ironi) çalıştırmak için PowerShell'i kullanmanın ne kadar zor olduğu konusunda bu Microsoft Connect raporuna da bakın.

http://connect.microsoft.com/PowerShell/feedback/details/376207/

--%PowerShell'i metni sağa yorumlamaya çalışmaktan vazgeçmek için bir yol olarak kullanmanızı önerirler .

Örneğin:

MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj

1
Ah, Microsoft interneti kırdı ve bağlantı artık geçerli değil.
Johan Boulé

1
Yukarıdaki bağlantı, archive.org adresinde web.archive.org/web/20131122050220/http://…
mwfearnley

4

Kaldırma dizeleri için kayıt defterini ayrıştırmaya ve yürütmeye çalışırken kabul edilen yanıt benim için çalışmıyor. Ne de olsa aramaya ihtiyacım olmadığı ortaya çıktı Invoke-Expression.

Sonunda kaldırma dizeleri yürütmek nasıl görmek için bu güzel şablon rastladı :

$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$app = 'MyApp'
$apps= @{}
Get-ChildItem $path | 
    Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} | 
    ForEach-Object -process {$apps.Set_Item(
        $_.getvalue('UninstallString'),
        $_.getvalue('DisplayName'))
    }

foreach ($uninstall_string in $apps.GetEnumerator()) {
    $uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
    & $uninstall_app $uninstall_arg
}

Bu benim için çalışıyor, çünkü $appbildiğim bir şirket içi uygulama sadece iki argümana sahip olacak. Daha karmaşık kaldırma dizeleri için birleştirme işlecini kullanmak isteyebilirsiniz . Ayrıca, sadece bir karma harita kullandım, ama gerçekten, muhtemelen bir dizi kullanmak istersiniz.

Ayrıca, aynı uygulamanın birden çok sürümü yüklüyse, bu kaldırıcı bir kerede hepsi arasında dolaşır MsiExec.exe, bu da kafa karıştırır , bu yüzden de var.


1

Çağrı operatörünü kullanmak istiyorsanız, bağımsız değişkenler bir değişkende saklanan bir dizi olabilir:

$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs

Çağrı operatörü ApplicationInfo nesneleriyle de çalışır.

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
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.