คุณสามารถใช้คุณสมบัติของวัตถุในสตริงที่ยกมาสองครั้งได้อย่างไร?


102

ฉันมีรหัสต่อไปนี้:

$DatabaseSettings = @();
$NewDatabaseSetting = "" | select DatabaseName, DataFile, LogFile, LiveBackupPath;
$NewDatabaseSetting.DatabaseName = "LiveEmployees_PD";
$NewDatabaseSetting.DataFile = "LiveEmployees_PD_Data";
$NewDatabaseSetting.LogFile = "LiveEmployees_PD_Log";
$NewDatabaseSetting.LiveBackupPath = '\\LiveServer\LiveEmployeesBackups';
$DatabaseSettings += $NewDatabaseSetting;

เมื่อฉันพยายามใช้คุณสมบัติอย่างใดอย่างหนึ่งในคำสั่ง string execute:

& "$SQlBackupExePath\SQLBackupC.exe" -I $InstanceName -SQL `
  "RESTORE DATABASE $DatabaseSettings[0].DatabaseName FROM DISK = '$tempPath\$LatestFullBackupFile' WITH NORECOVERY, REPLACE, MOVE '$DataFileName' TO '$DataFilegroupFolder\$DataFileName.mdf', MOVE '$LogFileName' TO '$LogFilegroupFolder\$LogFileName.ldf'"

มันพยายามแค่ใช้ค่าของ$DatabaseSettingsมากกว่าค่า$DatabaseSettings[0].DatabaseNameซึ่งไม่ถูกต้อง
วิธีแก้ปัญหาของฉันคือการคัดลอกไปยังตัวแปรใหม่

ฉันจะเข้าถึงคุณสมบัติของวัตถุโดยตรงในสตริงที่ยกมาสองครั้งได้อย่างไร

คำตอบ:


174

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

$foo = 2
"$foo"

กลายเป็น

"2"

หากคุณไม่ต้องการให้คุณต้องใช้เครื่องหมายคำพูดเดี่ยว:

$foo = 2
'$foo'

อย่างไรก็ตามหากคุณต้องการเข้าถึงคุณสมบัติหรือใช้ดัชนีกับตัวแปรในสตริงที่มีเครื่องหมายอัญประกาศคู่คุณต้องใส่นิพจน์ย่อยนั้นใน$():

$foo = 1,2,3
"$foo[1]"     # yields "1 2 3[1]"
"$($foo[1])"  # yields "2"

$bar = "abc"
"$bar.Length"    # yields "abc.Length"
"$($bar.Length)" # yields "3"

PowerShell จะขยายตัวแปรในกรณีเหล่านั้นเท่านั้นไม่มีอะไรเพิ่มเติม ในการบังคับให้ประเมินนิพจน์ที่ซับซ้อนมากขึ้นรวมถึงดัชนีคุณสมบัติหรือแม้แต่การคำนวณที่สมบูรณ์คุณต้องใส่สิ่งเหล่านั้นไว้ในตัวดำเนินการนิพจน์ย่อย$( )ซึ่งทำให้นิพจน์ภายในถูกประเมินและฝังไว้ในสตริง


วิธีนี้ใช้งานได้ แต่ฉันสงสัยว่าฉันอาจจะเชื่อมต่อสตริงได้หรือไม่เพราะฉันพยายามหลีกเลี่ยงตั้งแต่แรก
ozzy432836

2
@ ozzy432836 คุณทำได้แน่นอน หรือใช้สตริงรูปแบบ มันมักจะไม่สำคัญและขึ้นอยู่กับความชอบส่วนบุคคล
Joey

15

@Joey มีคำตอบที่ถูกต้อง แต่เพื่อเพิ่มอีกเล็กน้อยว่าทำไมคุณต้องบังคับให้ประเมินด้วย$():

โค้ดตัวอย่างของคุณมีความคลุมเครือที่ชี้ให้เห็นว่าเหตุใดผู้สร้าง PowerShell จึงอาจเลือกที่จะ จำกัด การขยายให้เป็นเพียงการอ้างอิงตัวแปรเท่านั้นและไม่สนับสนุนการเข้าถึงคุณสมบัติเช่นกัน (นอกเหนือจาก: การขยายสตริงทำได้โดยการเรียกใช้ToString()เมธอดบนวัตถุซึ่ง สามารถอธิบายผลลัพธ์ "แปลก" ได้)

ตัวอย่างของคุณอยู่ท้ายบรรทัดคำสั่ง:

...\$LogFileName.ldf

หากคุณสมบัติของอ็อบเจ็กต์ถูกขยายโดยค่าเริ่มต้นข้างต้นจะแก้ไขได้

...\

ตั้งแต่วัตถุอ้างอิงโดย$LogFileNameจะไม่ได้มีคุณสมบัติที่เรียกว่าldf, $null(หรือสตริงที่ว่างเปล่า) จะถูกแทนสำหรับตัวแปร


1
หาได้ดี ที่จริงผมก็ไม่แน่ใจว่าปัญหาของเขาคือ แต่พยายามที่จะเข้าถึงคุณสมบัติจากภายในสตริงกลิ่นไม่ดี :)
โจอี้

ขอบคุณเพื่อน! ทำไมคุณถึงคิดว่าการเข้าถึงคุณสมบัติในสตริงจึงมีกลิ่นไม่ดี? จะแนะนำว่าทำอย่างไร?
caveman_dick

มนุษย์ถ้ำ: มันเป็นเพียงสิ่งที่ดึงดูดสายตาของฉันว่าเป็นสาเหตุของข้อผิดพลาดที่อาจเกิดขึ้น คุณสามารถทำได้อย่างแน่นอนและไม่มีอะไรแปลก แต่เมื่อละเว้นตัวดำเนินการ $ () มันจะกรีดร้องว่า "Fail" เพราะมันไม่สามารถทำงานได้ ดังนั้นคำตอบของฉันจึงเป็นเพียงการคาดเดาที่มีการศึกษาเกี่ยวกับปัญหาของคุณโดยใช้สาเหตุความล้มเหลวแรกที่เป็นไปได้ที่ฉันเห็น ขออภัยหากดูเหมือน แต่ความคิดเห็นนั้นไม่ได้อ้างถึงแนวทางปฏิบัติที่ดีที่สุดหรือมากกว่านั้น
Joey

ดีคุณมีฉันเป็นห่วงที่นั่น! ;) เมื่อฉันเริ่มใช้ powershell ครั้งแรกฉันคิดว่าตัวแปรในสตริงค่อนข้างแปลก แต่ก็มีประโยชน์มาก ฉันไม่สามารถหาสถานที่ที่ชัดเจนที่ฉันสามารถค้นหาความช่วยเหลือด้านไวยากรณ์ได้
caveman_dick

10

หมายเหตุเอกสาร: Get-Help about_Quoting_Rulesครอบคลุมการแก้ไขสตริง แต่สำหรับ PSv5 ไม่ใช่เชิงลึก

เพื่อเสริมคำตอบที่เป็นประโยชน์ของ Joeyด้วยการสรุปเชิงปฏิบัติเกี่ยวกับการขยายสตริงของ PowerShell (การแก้ไขสตริงในสตริงที่ยกมาสองครั้ง ( "..."หรือที่เรียกว่าสตริงที่ขยายได้ ) รวมถึงสตริงที่ยกมาสองครั้งที่นี่ ):

  • อ้างอิงเท่านั้นเช่น$foo, $global:foo(หรือ$script:foo, ... ) และ$env:PATH (ตัวแปรสภาพแวดล้อม) รับรู้เมื่อได้โดยตรงที่ฝังอยู่ใน"..."สตริง - ที่เป็นเพียงการอ้างอิงตัวแปรตัวเองมีการขยายโดยไม่คำนึงถึงสิ่งต่อไปนี้

    • หากต้องการแยกความคลุมเครือของชื่อตัวแปรจากอักขระที่ตามมาในสตริงให้ใส่{}ชื่อตัวแปรและ ; เช่น${foo}.
      นี้เป็นสิ่งสำคัญโดยเฉพาะอย่างยิ่งถ้าชื่อตัวแปรตามด้วย:เช่น PowerShell มิฉะนั้นจะพิจารณาทุกอย่างระหว่าง$และขอบเขตระบุโดยทั่วไปแล้วจะก่อให้เกิดการแก้ไขที่จะล้มเหลว ; เช่นหยุดพัก แต่ทำงานได้ตามที่ตั้งใจไว้ (หรือมิฉะนั้น-escape : ):"$HOME: where the heart is.""${HOME}: where the heart is."
      `:"$HOME`: where the heart is."

    • ในการถือว่า a $หรือ a "เป็นตัวอักษรให้นำหน้าด้วยอักขระหลีก `( แบ็คทิก ); เช่น:
      "`$HOME's value: `"$HOME`""

  • สำหรับสิ่งอื่นใดรวมถึงการใช้ตัวห้อยอาร์เรย์และการเข้าถึงคุณสมบัติของตัวแปรอ็อบเจ็กต์คุณต้องใส่นิพจน์ใน$(...)ตัวดำเนินการนิพจน์ย่อย (เช่น"PS version: $($PSVersionTable.PSVersion)"หรือ"1st el.: $($someArray[0])")

    • การใช้$(...)even ช่วยให้คุณสามารถฝังเอาต์พุตจากบรรทัดคำสั่งทั้งหมดในสตริง double-quoted (เช่น"Today is $((Get-Date).ToString('d')).")
  • ผลลัพธ์การแก้ไขไม่จำเป็นต้องมีลักษณะเหมือนกับรูปแบบเอาต์พุตเริ่มต้น (สิ่งที่คุณจะเห็นหากคุณพิมพ์ตัวแปร / นิพจน์ย่อยโดยตรงไปยังคอนโซลซึ่งเกี่ยวข้องกับฟอร์แมตเตอร์เริ่มต้นโปรดดูGet-Help about_format.ps1xml):

    • คอลเลกชันรวมถึงอาร์เรย์จะถูกแปลงเป็นสตริงโดยการวางช่องว่างเดียวระหว่างการแสดงสตริงขององค์ประกอบ (โดยค่าเริ่มต้นสามารถระบุตัวคั่นอื่นได้โดยการตั้งค่า$OFS) เช่น"array: $(@(1, 2, 3))"อัตราผลตอบแทนarray: 1 2 3

    • กรณีของประเภทอื่น ๆ (รวมถึงองค์ประกอบของคอลเลกชันที่ไม่ได้เป็นตัวเองคอลเลกชัน) จะstringified โดยทั้งเรียกIFormattable.ToString()วิธีกับวัฒนธรรมคงถ้าประเภทเช่นที่สนับสนุนIFormattableอินเตอร์เฟซ[1] , หรือโดยการเรียก.psobject.ToString()ซึ่งในกรณีส่วนใหญ่ก็จะเรียก.ToString()วิธีการของประเภท. NET [2]ซึ่งอาจให้หรือไม่อาจให้การแสดงที่มีความหมาย: เว้นแต่ว่าประเภท (ไม่ใช่แบบดั้งเดิม) ได้แทนที่.ToString()เมธอดนี้โดยเฉพาะสิ่งที่คุณจะได้รับคือชื่อเต็ม(เช่น"hashtable: $(@{ key = 'value' })"อัตราผลตอบแทนhashtable: System.Collections.Hashtable)

    • ที่จะได้รับผลเช่นเดียวกับในคอนโซลใช้ subexpression และท่อOut-Stringและนำไปใช้.Trim()ในการลบบรรทัดว่างใด ๆ ชั้นนำและต่อท้ายถ้าต้องการ; เช่น
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())"ผลตอบแทน:

          hashtable:                                                                                                                                                                          
          Name                           Value                                                                                                                                               
          ----                           -----                                                                                                                                               
          key                            value      
      

[1] พฤติกรรมที่น่าแปลกใจนี้หมายความว่าสำหรับประเภทที่สนับสนุนการแสดงที่มีความอ่อนไหวต่อวัฒนธรรม$obj.ToString()จะให้การแสดงที่เหมาะสมกับวัฒนธรรมในปัจจุบันในขณะที่"$obj"(การแก้ไขสตริง) จะส่งผลให้เกิดการแสดงที่ไม่แปรเปลี่ยนตามวัฒนธรรมเสมอ- ดูคำตอบนี้

[2] การลบล้างที่โดดเด่น:

  • ที่กล่าวถึงก่อนหน้านี้ stringification ของคอลเลกชัน (รายการพื้นที่ที่แยกออกจากกันขององค์ประกอบมากกว่าสิ่งที่ต้องการSystem.Object[])
  • hashtable- เหมือนเป็นตัวแทนของ[pscustomobject]อินสแตนซ์ (อธิบายที่นี่ ) มากกว่าสตริงที่ว่างเปล่า

9

@ โจอี้มีคำตอบดีๆ มีอีกวิธีหนึ่งที่มีรูปลักษณ์. NET มากขึ้นด้วย String.Format ที่เทียบเท่าฉันชอบเมื่อเข้าถึงคุณสมบัติบนวัตถุ:

สิ่งที่เกี่ยวกับรถยนต์:

$properties = @{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; }

สร้างวัตถุ:

$car = New-Object -typename psobject -Property $properties

แก้ไขสตริง:

"The {0} car is a nice {1} that is {2}" -f $car.color, $car.type, $car.package

ผลลัพธ์:

# The red car is a nice sedan that is fully loaded

ใช่ว่าจะอ่านได้ง่ายขึ้นโดยเฉพาะถ้าคุณคุ้นเคยกับ. net code ขอบคุณ! :)
caveman_dick

3
มี gotcha ที่ต้องระวังเมื่อคุณใช้สตริงรูปแบบเป็นครั้งแรกซึ่งคุณมักจะต้องใส่นิพจน์ใน parens ตัวอย่างเช่นคุณไม่สามารถเขียนได้write-host "foo = {0}" -f $fooเนื่องจาก PowerShell จะถือว่า-fเป็นForegroundColorพารามิเตอร์สำหรับwrite-host. write-host ( "foo = {0}" -f $foo )ในตัวอย่างนี้คุณจะต้อง นี่เป็นพฤติกรรมมาตรฐานของ PowerShell แต่ควรสังเกต
andyb

ตัวอย่างข้างต้นไม่ใช่ "การแก้ไขสตริง" แต่เป็น "การจัดรูปแบบคอมโพสิต" เนื่องจาก Powershell เชื่อมโยงกับ. NET อย่างแยกไม่ออกซึ่ง C # และ VB.NET เป็นภาษาการเขียนโปรแกรมหลักคำว่า "การแก้ไขสตริง" และ "สตริงที่ถูกแทรก" (และสิ่งที่คล้ายกัน) ควรใช้เฉพาะกับสตริงที่มี $ นำหน้าใหม่ที่พบใน ภาษา (C # 6 และ VB.NET 14) String.Formatในสายเหล่านี้แสดงออกพารามิเตอร์ที่ฝังตัวอยู่ในสตริงโดยตรงแทนการอ้างอิงพารามิเตอร์ที่เป็นตัวเลขที่มีการแสดงออกที่เกิดขึ้นจริงแล้วเป็นข้อโต้แย้ง
Uber Kluger

@andyb เกี่ยวกับรูปแบบสตริง "gotchas" นี่คือความแตกต่างระหว่างโหมดExpressionและArgument (ดูabout_Parsing ) คำสั่งจะแยกวิเคราะห์เป็นโทเค็น (กลุ่มของอักขระที่สร้างสตริงที่มีความหมาย) ช่องว่างจะแยกโทเค็นที่จะรวมเข้าด้วยกัน แต่จะถูกละเว้น หากโทเค็นที่ 1 ของคำสั่งเป็นตัวเลขตัวแปรคีย์เวิร์ดคำสั่ง (if, while, etc), unary operator, {, etc ... ดังนั้นโหมดการแยกวิเคราะห์จะเป็นExpressionอย่างอื่นArgument(ขึ้นอยู่กับ command terminator)
Uber Kluger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.