Lưu ý: Lệnh trong câu hỏi sử dụng Start-Process
, ngăn chặn việc bắt trực tiếp đầu ra của chương trình đích. Nói chung, không sử dụng Start-Process
để thực thi các ứng dụng bảng điều khiển một cách đồng bộ - chỉ cần gọi chúng trực tiếp , như trong bất kỳ trình bao nào. Làm như vậy để giữ cho ứng dụng được kết nối với các luồng tiêu chuẩn của bảng điều khiển cuộc gọi, cho phép thu được đầu ra của nó bằng cách gán đơn giản $output = netdom ...
, như chi tiết bên dưới.
Về cơ bản , việc bắt đầu ra từ các tiện ích bên ngoài hoạt động giống như với các lệnh gốc PowerShell (bạn có thể muốn xem lại cách thực hiện các công cụ bên ngoài ):
$cmdOutput = <command> # captures the command's success stream / stdout
Lưu ý rằng $cmdOutput
nhận được một mảng các đối tượng nếu <command>
tạo ra nhiều hơn 1 đối tượng đầu ra , trong trường hợp chương trình bên ngoài có nghĩa là một chuỗi chuỗi chứa các dòng đầu ra của chương trình .
Nếu bạn muốn $cmdOutput
để luôn nhận một đơn - có khả năng nhiều dòng - chuỗi , sử dụng
$cmdOutput = <command> | Out-String
Để chụp đầu ra trong một biến và in ra màn hình :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Hoặc, nếu <command>
là một lệnh ghép ngắn hoặc hàm nâng cao , bạn có thể sử dụng tham số chung
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Lưu ý rằng với -OutVariable
, không giống như trong các kịch bản khác, $cmdOutput
là luôn luôn một bộ sưu tập , ngay cả khi chỉ một đối tượng là đầu ra. Cụ thể, một thể hiện của [System.Collections.ArrayList]
kiểu giống như mảng được trả về.
Xem vấn đề GitHub này để thảo luận về sự khác biệt này.
Để nắm bắt đầu ra từ nhiều lệnh , hãy sử dụng một biểu thức con ( $(...)
) hoặc gọi một khối tập lệnh ( { ... }
) bằng &
hoặc .
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Lưu ý rằng cần phải có tiền tố chung với &
(toán tử cuộc gọi) một lệnh riêng có tên / đường dẫn được trích dẫn - ví dụ: $cmdOutput = & 'netdom.exe' ...
- không liên quan đến các chương trình bên ngoài (nó áp dụng tương tự cho các tập lệnh PowerShell), nhưng là một yêu cầu cú pháp : PowerShell phân tích cú pháp một câu lệnh bắt đầu bằng một chuỗi trích dẫn trong chế độ biểu thức theo mặc định, trong khi chế độ đối số là cần thiết để gọi các lệnh (lệnh ghép ngắn, chương trình bên ngoài, hàm, bí danh), đó là những gì &
đảm bảo.
Sự khác biệt chính giữa $(...)
và & { ... }
/ . { ... }
là cái trước thu thập tất cả đầu vào trong bộ nhớ trước khi trả lại toàn bộ, trong khi dòng sau phát ra đầu ra, phù hợp cho xử lý đường ống từng cái một.
Chuyển hướng cũng hoạt động tương tự, về cơ bản (nhưng xem phần bên dưới):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
Tuy nhiên, đối với các lệnh bên ngoài, phần sau có khả năng hoạt động như mong đợi:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Cân nhắc cụ thể cho các chương trình bên ngoài :
Các chương trình bên ngoài , vì chúng hoạt động bên ngoài hệ thống loại của PowerShell, chỉ bao giờ trả về chuỗi thông qua luồng thành công (stdout).
Nếu đầu ra chứa nhiều hơn 1 dòng , PowerShell bằng cách chia mặc định nó thành một mảng các chuỗi . Chính xác hơn, các dòng đầu ra được lưu trữ trong một mảng kiểu [System.Object[]]
có các phần tử là chuỗi ( [System.String]
).
Nếu bạn muốn đầu ra là một single , có khả năng nhiều đường dây , đường ống đểOut-String
:
$cmdOutput = <command> | Out-String
Chuyển hướng stderr sang stdout với2>&1
, để cũng nắm bắt nó như là một phần của dòng thành công, đi kèm với cảnh báo :
Để tạo 2>&1
stdout và stderr hợp nhất tại nguồn , hãy cmd.exe
xử lý chuyển hướng , sử dụng các thành ngữ sau:
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string
cmd /c
gọi cmd.exe
với lệnh <command>
và thoát sau khi <command>
đã kết thúc.
- Lưu ý các trích dẫn đơn xung quanh
2>&1
, điều này đảm bảo rằng chuyển hướng được chuyển đến cmd.exe
thay vì được PowerShell diễn giải.
Lưu ý rằng liên quan cmd.exe
có nghĩa là các quy tắc thoát ký tự và mở rộng biến môi trường của nó xuất hiện, theo mặc định bên cạnh các yêu cầu riêng của PowerShell; trong PS v3 +, bạn có thể sử dụng tham số đặc biệt --%
( ký hiệu phân tích cú pháp dừng ) để tắt giải thích các tham số còn lại bằng PowerShell, ngoại trừ cmd.exe
các tham chiếu biến môi trường kiểu như, chẳng hạn như %PATH%
.
Lưu ý rằng vì bạn đang hợp nhất thiết bị xuất chuẩn và thiết bị xuất chuẩn tại nguồn với cách tiếp cận này, bạn sẽ không thể phân biệt giữa các dòng có nguồn gốc xuất chuẩn và thiết bị xuất chuẩn trong PowerShell; nếu bạn cần sự khác biệt này, hãy sử dụng 2>&1
chuyển hướng riêng của PowerShell - xem bên dưới.
Sử dụng chuyển hướng của PowerShell 2>&1
để biết dòng nào đến từ luồng nào :
Stderr sản lượng được chụp như ghi lỗi ( [System.Management.Automation.ErrorRecord]
), không dây, do đó mảng đầu ra có thể chứa một sự pha trộn của chuỗi (mỗi chuỗi đại diện cho một dòng stdout) và hồ sơ lỗi (mỗi bản ghi đại diện cho một dòng stderr) . Lưu ý rằng, theo yêu cầu của 2>&1
cả chuỗi và bản ghi lỗi đều được nhận qua luồng đầu ra thành công của PowerShell ).
Trong bảng điều khiển, các bản ghi lỗi được in màu đỏ và cái thứ nhất theo mặc định tạo ra màn hình đa dòng , theo cùng định dạng mà lỗi không kết thúc của cmdlet sẽ hiển thị; các bản ghi lỗi tiếp theo cũng in màu đỏ, nhưng chỉ in thông báo lỗi của chúng trên một dòng duy nhất .
Khi xuất ra bàn điều khiển , các chuỗi thường đứng đầu trong mảng đầu ra, theo sau là các bản ghi lỗi (ít nhất là trong một loạt các đầu ra dòng stdout / stderr "cùng một lúc"), nhưng may mắn thay, khi bạn nắm bắt được đầu ra , nó được xen kẽ đúng cách , sử dụng cùng một thứ tự đầu ra mà bạn sẽ nhận được mà không có 2>&1
; nói cách khác: khi xuất ra bàn điều khiển , đầu ra bị bắt KHÔNG phản ánh thứ tự trong đó các dòng stdout và stderr được tạo bởi lệnh bên ngoài.
Nếu bạn nắm bắt được toàn bộ đầu ra trong một đơn chuỗi vớiOut-String
, PowerShell sẽ thêm dòng thêm , bởi vì chuỗi đại diện của một bản ghi lỗi chứa thông tin bổ sung như vị trí ( At line:...
) và loại ( + CategoryInfo ...
); tò mò, điều này chỉ áp dụng cho bản ghi lỗi đầu tiên .
- Để khắc phục sự cố này, áp dụng
.ToString()
phương thức cho từng đối tượng đầu ra thay vì đường ống đến Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
trong PS v3 +, bạn có thể đơn giản hóa thành:
$cmdOutput = <command> 2>&1 | % ToString
(Như một phần thưởng, nếu đầu ra không được ghi lại, điều này tạo ra đầu ra xen kẽ đúng cách ngay cả khi in ra bàn điều khiển.)
Ngoài ra, hãy lọc các bản ghi lỗi ra và gửi chúng đến luồng lỗi của PowerShell vớiWrite-Error
(như một phần thưởng, nếu đầu ra không được ghi lại, điều này tạo ra đầu ra xen kẽ chính xác ngay cả khi in ra bàn điều khiển):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
để thực thi các ứng dụng bảng điều khiển (theo định nghĩa bên ngoài) một cách đồng bộ - chỉ cần gọi chúng trực tiếp , như trong bất kỳ shell nào; dí dỏm :netdom /verify $pc /domain:hosp.uhhg.org
. Làm như vậy sẽ giữ cho ứng dụng được kết nối với các luồng tiêu chuẩn của bảng điều khiển cuộc gọi, cho phép thu được đầu ra của nó bằng cách gán đơn giản$output = netdom ...
. Hầu hết các câu trả lời dưới đây hoàn toàn từ bỏStart-Process
ủng hộ thực hiện trực tiếp.