Làm thế nào bạn có thể sử dụng thuộc tính của một đối tượng trong một chuỗi được trích dẫn kép?


96

Tôi có mã sau:

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

Khi tôi cố gắng sử dụng một trong các thuộc tính trong lệnh thực thi chuỗi:

& "$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'"

Nó cố gắng chỉ sử dụng giá trị của $DatabaseSettingsthay vì giá trị của $DatabaseSettings[0].DatabaseName, giá trị này không hợp lệ.
Cách giải quyết của tôi là sao chép nó vào một biến mới.

Làm cách nào tôi có thể truy cập trực tiếp thuộc tính của đối tượng trong một chuỗi dấu ngoặc kép?

Câu trả lời:


163

Khi bạn đặt một tên biến trong một chuỗi được trích dẫn kép, nó sẽ được thay thế bằng giá trị của biến đó:

$foo = 2
"$foo"

trở thành

"2"

Nếu bạn không muốn, bạn phải sử dụng dấu ngoặc kép:

$foo = 2
'$foo'

Tuy nhiên, nếu bạn muốn truy cập thuộc tính hoặc sử dụng chỉ mục trên các biến trong một chuỗi được trích dẫn kép, bạn phải gửi biểu thức con đó vào $():

$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 chỉ mở rộng các biến trong những trường hợp đó, không có gì hơn. Để buộc đánh giá các biểu thức phức tạp hơn, bao gồm các chỉ mục, thuộc tính hoặc thậm chí là các phép tính hoàn chỉnh, bạn phải đặt chúng trong toán tử biểu thức con, điều $( )này khiến biểu thức bên trong được đánh giá và nhúng vào chuỗi.


Tác phẩm này nhưng tôi tự hỏi, nếu tôi có thể cũng chỉ chuỗi concatenate khi tôi đang cố gắng để tránh ở nơi đầu tiên
ozzy432836

2
@ ozzy432836 Tất nhiên là bạn có thể. Hoặc sử dụng chuỗi định dạng. Nó thường không thực sự quan trọng và phụ thuộc vào sở thích cá nhân.
Joey

15

@Joey có câu trả lời chính xác, nhưng chỉ cần thêm một chút về lý do tại sao bạn cần phải đánh giá $() :

Mã ví dụ của bạn chứa một sự mơ hồ chỉ ra lý do tại sao các nhà sản xuất PowerShell có thể đã chọn giới hạn mở rộng cho các tham chiếu biến đơn thuần và không hỗ trợ quyền truy cập vào các thuộc tính (ngoài ra: mở rộng chuỗi được thực hiện bằng cách gọi ToString()phương thức trên đối tượng, có thể giải thích một số kết quả "kỳ quặc").

Ví dụ của bạn có ở cuối dòng lệnh:

...\$LogFileName.ldf

Nếu thuộc tính của các đối tượng được mở rộng theo mặc định, thì điều trên sẽ giải quyết thành

...\

vì đối tượng được tham chiếu bởi $LogFileNamesẽ không có thuộc tính được gọi ldf, $null(hoặc một chuỗi rỗng) sẽ được thay thế cho biến.


1
Rất vui. Tôi thực sự không phải là hoàn toàn chắc chắn vấn đề của ông là gì nhưng cố gắng để tính truy cập từ bên trong một chuỗi mùi xấu :)
Joey

Cảm ơn các bạn! Johannes tại sao bạn nghĩ việc truy cập các thuộc tính trong một chuỗi có mùi khó chịu? Làm thế nào đề nghị nó được thực hiện?
caveman_dick

thượng cổ: Đó chỉ là một thứ đập vào mắt tôi vì có thể là một nguyên nhân lỗi. Bạn chắc chắn có thể làm điều đó và không có gì lạ, nhưng khi bỏ qua toán tử $ (), nó sẽ kêu lên "Fail" vì nó không thể hoạt động. Vì vậy, câu trả lời của tôi chỉ là một phỏng đoán có học về vấn đề của bạn có thể là gì, sử dụng nguyên nhân thất bại đầu tiên mà tôi có thể thấy. Xin lỗi nếu nó trông giống như nó, nhưng nhận xét đó không đề cập đến bất kỳ phương pháp hay nhất nào.
Joey

Tốt bạn đã để tôi lo lắng ở đó! ;) Khi tôi lần đầu tiên bắt đầu sử dụng powershell, tôi nghĩ biến trong một chuỗi hơi kỳ lạ, nhưng nó khá tiện dụng. Tôi chỉ không tìm được một nơi chắc chắn mà tôi có thể tìm kiếm trợ giúp về cú pháp.
caveman_dick

9

@Joey có một câu trả lời hay. Có một cách khác với giao diện .NET hơn với tương đương String.Format, tôi thích nó hơn khi truy cập các thuộc tính trên các đối tượng:

Những điều về một chiếc xe hơi:

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

Tạo một đối tượng:

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

Nội suy một chuỗi:

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

Kết quả đầu ra:

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

Vâng, điều đó dễ đọc hơn, đặc biệt nếu bạn đã quen với mã .net. Cảm ơn! :)
caveman_dick

3
Có một điểm cần lưu ý khi bạn lần đầu tiên sử dụng chuỗi định dạng, đó là bạn thường sẽ cần phải bao gồm biểu thức trong parens. Ví dụ, bạn không thể viết đơn giản write-host "foo = {0}" -f $foovì PowerShell sẽ được coi -fForegroundColortham số cho write-host. Trong ví dụ này, bạn sẽ cần write-host ( "foo = {0}" -f $foo ). Đây là hành vi PowerShell tiêu chuẩn, nhưng đáng chú ý.
andyb

Nói một cách dễ hiểu, ví dụ trên không phải là "nội suy chuỗi", nó là "định dạng hỗn hợp". Vì Powershell bị ràng buộc chặt chẽ với .NET mà C # và VB.NET là ngôn ngữ lập trình chính, các thuật ngữ "nội suy chuỗi" và "chuỗi nội suy" (và bất kỳ điều gì tương tự) chỉ nên được áp dụng cho các chuỗi $ thêm trước mới được tìm thấy trong các ngôn ngữ (C # 6 và VB.NET 14). Trong các chuỗi này, biểu thức tham số được nhúng trực tiếp vào chuỗi thay vì tham chiếu tham số số, với các biểu thức thực tế sau đó là đối số String.Format.
Uber Kluger

@andyb, liên quan đến chuỗi định dạng "gotchas". Đây thực sự là sự khác biệt giữa chế độ Biểu thứcĐối số (xem about_Parsing ). Các lệnh được phân tích cú pháp thành các mã thông báo (các nhóm ký tự tạo thành một chuỗi có nghĩa). Dấu cách phân tách các mã thông báo sẽ hợp nhất nhưng bị bỏ qua. Nếu mã thông báo đầu tiên của lệnh là một số, biến, từ khóa câu lệnh (nếu, trong khi, v.v.), toán tử một ngôi, {, v.v. thì chế độ phân tích cú pháp là Expressionngược lại Argument(tối đa một dấu chấm lệnh).
Uber Kluger

8

Ghi chú tài liệu: Get-Help about_Quoting_Rulesbao gồm nội suy chuỗi, nhưng, đối với PSv5, không chuyên sâu.

Để bổ sung cho câu trả lời hữu ích của Joey với một bản tóm tắt thực dụng về mở rộng chuỗi của PowerShell (nội suy chuỗi trong các chuỗi được trích dẫn kép ( "..."), bao gồm cả trong chuỗi được trích dẫn kép ở đây ):

  • Chỉ các tham chiếu như $foo, $global:foo(hoặc $script:foo, ...) và$env:PATH (biến môi trường) mới được nhận dạng khi được nhúng trực tiếp vào một "..."chuỗi - nghĩa là chỉ bản thân tham chiếu biến được mở rộng, bất kể những gì tiếp theo.

    • Để phân biệt một tên biến với các ký tự tiếp theo trong chuỗi, hãy đặt nó vào {} ; vd ${foo}.
      Điều này đặc biệt quan trọng nếu tên biến được theo sau bởi: , vì PowerShell nếu không sẽ xem xét mọi thứ giữa bộ chỉ định phạm vi$:một , thường khiến nội suy không thành công ; ví dụ: nghỉ, nhưng hoạt động như dự định. (Ngoài ra, -escape sự : )."$HOME: where the heart is.""${HOME}: where the heart is."
      `:"$HOME`: where the heart is."

    • Để điều trị một $ hoặc a "là một nghĩa đen , hãy đặt tiền tố nó bằng ký tự thoát. `(một que tính ngược ); ví dụ:
      "`$HOME's value: `"$HOME`""

  • Đối với bất kỳ điều gì khác, bao gồm cả việc sử dụng chỉ số con mảng và truy cập thuộc tính của một biến đối tượng , bạn phải đưa biểu thức vào$(...) , toán tử biểu thức con (ví dụ: "PS version: $($PSVersionTable.PSVersion)"hoặc "1st el.: $($someArray[0])")

    • Sử dụng $(...)chẵn cho phép bạn nhúng đầu ra từ toàn bộ dòng lệnh trong các chuỗi được trích dẫn kép (ví dụ "Today is $((Get-Date).ToString('d')).":).
  • Kết quả nội suy không nhất thiết phải giống với định dạng đầu ra mặc định (ví dụ bạn sẽ thấy nếu bạn in biến / biểu thức con trực tiếp vào bảng điều khiển, liên quan đến định dạng mặc định; xem Get-Help about_format.ps1xml):

    • Bộ sưu tập , bao gồm cả mảng, được chuyển đổi thành chuỗi bằng cách đặt một khoảng trắng giữa các biểu diễn chuỗi của các phần tử (theo mặc định; một dấu phân tách khác có thể được chỉ định bằng cách thiết lập $OFS) Ví dụ: kết "array: $(@(1, 2, 3))"quảarray: 1 2 3

    • Trường hợp của bất kỳ loại khác (bao gồm cả các yếu tố của bộ sưu tập mà không phải là bản thân các bộ sưu tập) đều được chuyển đổi thành chuỗi bằng một trong hai cách gọi IFormattable.ToString()phương thức với các nền văn hóa bất biến , nếu loại của dụ hỗ trợ các IFormattablegiao diện [1] , hoặc bằng cách gọi .psobject.ToString(), mà trong nhiều trường hợp chỉ đơn giản gọi .ToString()phương thức của loại .NET cơ bản [2] , có thể có hoặc không thể cung cấp một biểu diễn có ý nghĩa: trừ khi một kiểu (không phải nguyên thủy) đã ghi đè .ToString()phương thức một cách cụ thể , tất cả những gì bạn sẽ nhận được là tên kiểu đầy đủ (ví dụ, "hashtable: $(@{ key = 'value' })"sản lượng hashtable: System.Collections.Hashtable).

    • Để có được đầu ra giống như trong bảng điều khiển , hãy sử dụng biểu thức con và đường ống dẫn đếnOut-String và áp dụng .Trim()để loại bỏ bất kỳ dòng trống nào ở đầu và cuối, nếu muốn; ví dụ,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())"sản lượng:

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

[1] Hành vi có lẽ đáng ngạc nhiên này có nghĩa là, đối với các loại hỗ trợ biểu diễn nhạy cảm với văn hóa, $obj.ToString()mang lại biểu diễn phù hợp với văn hóa hiện tại , trong khi "$obj"(nội suy chuỗi) luôn dẫn đến biểu diễn bất biến về văn hóa - hãy xem câu trả lời này của tôi.

[2]
Ghi đè đáng chú ý: * Việc xâu chuỗi các tập hợp đã được thảo luận trước đây (danh sách các phần tử được phân tách bằng dấu cách chứ không phải là một cái gì đó tương tự System.Object[]).
* Biểu diễn giống như bảng băm của các [pscustomobject]cá thể (giải thích ở đây ) chứ không phải là chuỗi rỗng .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.