คุณจะรันคำสั่งดั้งเดิมโดยพลการจากสตริงได้อย่างไร?


208

ฉันสามารถแสดงความต้องการของฉันกับสถานการณ์ต่อไปนี้: เขียนฟังก์ชั่นที่ยอมรับสตริงที่จะเรียกใช้เป็นคำสั่งดั้งเดิม

มันไม่ได้ดึงความคิดที่ไกลเกินไป: หากคุณกำลังเชื่อมต่อกับยูทิลิตี้บรรทัดคำสั่งอื่น ๆ จากที่อื่น ๆ ใน บริษัท ที่ให้คำสั่งให้คุณรันคำต่อคำ เพราะคุณไม่ได้ควบคุมคำสั่งคุณต้องยอมรับคำสั่งที่ถูกต้องเป็น input นี่คืออาการสะอึกหลักที่ฉันไม่สามารถเอาชนะได้ง่าย:

  1. คำสั่งอาจดำเนินการโปรแกรมที่อยู่ในพา ธ ที่มีช่องว่างอยู่:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
  2. คำสั่งอาจมีพารามิเตอร์ที่มีช่องว่างในพวกเขา:

    $command = 'echo "hello world!"';
  3. คำสั่งอาจมีทั้งเห็บเดี่ยวและคู่:

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

มีใดวิธีทำความสะอาดการบรรลุนี้หรือไม่? ฉันทำได้แค่คิดวิธีการฟุ่มเฟือยและการแก้ปัญหาที่น่าเกลียด แต่สำหรับภาษาสคริปต์ฉันรู้สึกว่ามันน่าจะง่าย

คำตอบ:


318

Invoke-Expressioniexนอกจากนี้ยังเป็นนามแฝง ต่อไปนี้จะใช้กับตัวอย่าง # 2 และ # 3 ของคุณ:

iex $command

สตริงบางตัวจะไม่ทำงานตามที่เป็นอยู่เช่นตัวอย่างที่ 1 ของคุณเนื่องจาก exe อยู่ในเครื่องหมายคำพูด สิ่งนี้จะทำงานได้ตามที่เป็นอยู่เนื่องจากเนื้อหาของสตริงเป็นวิธีที่คุณเรียกใช้โดยตรงจากพรอมต์คำสั่ง Powershell:

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

อย่างไรก็ตามหาก exe อยู่ในเครื่องหมายอัญประกาศคุณต้องการความช่วยเหลือใน&การทำให้มันทำงานได้ดังตัวอย่างในการรันจาก commandline:

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

แล้วในสคริปต์:

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

เป็นไปได้ว่าคุณสามารถจัดการเกือบทุกกรณีได้ด้วยการตรวจสอบว่าอักขระตัวแรกของสตริงคำสั่ง"เหมือนกับในการนำไปใช้แบบไร้เดียงสา:

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

แต่คุณอาจพบกรณีอื่นที่ต้องเรียกใช้ในวิธีที่แตกต่าง ในกรณีดังกล่าวคุณจะต้องใช้อย่างใดอย่างหนึ่งtry{}catch{}อาจจะเป็นประเภท / ข้อความยกเว้นเฉพาะหรือตรวจสอบสตริงคำสั่ง

หากคุณได้รับเส้นทางแบบสัมบูรณ์เสมอแทนที่จะเป็นเส้นทางแบบสัมพัทธ์คุณไม่ควรมีกรณีพิเศษมากมายนอกเหนือจาก 2 ข้อข้างต้น


3
นามแฝงเป็นเรื่องที่ดี จำไว้ว่าถ้าคุณย้ายไปที่เครื่องอื่นหรือส่งสคริปต์นั้นไปยังบุคคลอื่นที่ชื่อแทนอาจไม่ได้รับการตั้งค่า ต้องการชื่อเต็มของฟังก์ชั่น PowerShell
Doug Finke

1
@Doug: ส่วนใหญ่เวลาที่ฉันทำหรือใช้นามแฝงในตัว (โดยเฉพาะอย่างยิ่งสำหรับความกะทัดรัดใน commandline) evalสิ่งที่เป็นครึ่งล้อเล่นเพราะนั่นคือสิ่งที่เรียกว่าในหลาย ๆ ภาษาสคริปต์อื่น ๆ invoke-expressionและเรื่องนี้ไม่ได้เป็นคำถามแรกที่ผมเคยเห็นคนที่มีความคิดเกี่ยวกับ และกรณีของ OP นั้นดูเหมือนสคริปต์ภายในองค์กรเท่านั้น
Joel B Fant

ฉันได้ลองแล้ว แต่มันใช้ไม่ได้กับ: $ command = '"Media Player \ mplayer2.exe Files \ Windows C: \ Program" "H: \ Audio \ Music \ Stevie Wonder \ Stevie Wonder - Superstition.mp3" '
Johnny Kauffman

3
คุณอาจต้องใส่ "&" หรือ " ลงนามในคำสั่งก่อนที่จะเกิดขึ้นจริงถ้ามันไม่ได้ PowerShell พื้นเมืองเช่นInvoke-Expression "& $command"
Torbjörn Bergstedt

Torbjörn Bergstedt ชนะ! "&" เวทย์มนตร์พิเศษได้แก้ไขปัญหาของฉัน! เป็นผลให้ฉันทั้งสองมีความสุขและวุ่นวาย
Johnny Kauffman

19

โปรดดูรายงาน Microsoft Connect นี้เป็นหลักว่า blummin 'ยากแค่ไหนที่จะใช้ PowerShell เพื่อเรียกใช้คำสั่งเชลล์ (โอ้คนประชด)

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

พวกเขาแนะนำให้ใช้--%เป็นวิธีบังคับให้ PowerShell หยุดพยายามตีความข้อความทางด้านขวา

ตัวอย่างเช่น:

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

1
อ่าและ Microsoft ก็ขัดจังหวะอินเทอร์เน็ตและลิงก์ไม่ถูกต้องอีกต่อไป
Johan Boulé

1
ลิงก์ด้านบนอยู่ใน archive.org ที่web.archive.org/web/20131122050220/http://…
mwfearnley

4

คำตอบที่ยอมรับไม่ทำงานสำหรับฉันเมื่อพยายามแยกวิเคราะห์รีจิสทรีสำหรับถอนการติดตั้งสตริงและดำเนินการ ปรากฎว่าฉันไม่ต้องการการโทรติดต่อInvoke-Expressionเลย

ในที่สุดฉันก็พบกับเทมเพลตที่สวยงามนี้เพื่อดูวิธีการเรียกใช้สตริงการถอนการติดตั้ง:

$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
}

สิ่งนี้ใช้ได้กับฉันนั่น$appก็เพราะว่าเป็นแอปพลิเคชันภายในองค์กรที่ฉันรู้ว่าจะมีเพียงสองข้อโต้แย้งเท่านั้น สำหรับการถอนการติดตั้งที่ซับซ้อนมากขึ้นสตริงคุณอาจต้องการที่จะใช้ร่วมผู้ประกอบการ นอกจากนี้ฉันเพิ่งใช้แฮชแผนที่ แต่จริงๆแล้วคุณอาจต้องการใช้อาร์เรย์

นอกจากนี้ถ้าคุณมีหลายรุ่นของโปรแกรมเดียวกันที่ติดตั้งรอบนี้ถอนการติดตั้งพระทัยผ่านพวกเขาทั้งหมดในครั้งเดียวซึ่งสับสนMsiExec.exeเพื่อให้มีที่มากเกินไป


1

หากคุณต้องการใช้โอเปอเรเตอร์การโทรอาร์กิวเมนต์สามารถเป็นอาเรย์ที่จัดเก็บไว้ในตัวแปร:

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

ผู้ประกอบการโทรทำงานร่วมกับวัตถุ ApplicationInfo เช่นกัน

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.