Tập lệnh PowerShell chạy tập lệnh Python

Tôi đang làm việc với một nhóm đối tác tại Microsoft và họ giải thích rằng họ muốn phân tích các tập lệnh PowerShell từ Python. Cách tiếp cận tự nhiên của họ là gọi tệp thực thi PowerShell và xây dựng một dòng lệnh thực hiện những gì họ cần. Tôi nghĩ rằng có thể có một cách tốt hơn vì việc tạo một quy trình PowerShell mới mỗi lần rất tốn kém, vì vậy tôi bắt đầu thực hiện một số nghiên cứu để xem có thể thực hiện được điều gì đó. Tôi đã biết về IronPython (Python tích hợp chặt chẽ. NET) trong một thời gian dài và chúng tôi đã gặp Jim Hugunin ngay sau khi anh ấy đến Microsoft và PowerShell mới được triển khai, nhưng nhóm đang sử dụng cPython nên tôi đã tìm kiếm các mô-đun Python lưu trữ. NET và tìm thấy mô-đun

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
2

Gói pythonnet cung cấp cho các nhà phát triển Python quyền truy cập cực kỳ dễ dàng vào thời gian chạy dotnet từ Python. Tôi nghĩ rằng gói này có thể là chìa khóa để truy cập PowerShell, sau một số điều tra, tôi thấy rằng nó có chính xác thứ tôi cần để lưu trữ PowerShell trong tập lệnh Python

ruột

Tôi cần tìm ra cách tải công cụ PowerShell. Đầu tiên, có một số yêu cầu để làm cho tất cả điều này hoạt động. Dotnet phải có sẵn, cũng như PowerShell và

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
2 cung cấp một cách để chỉ định nơi tìm kiếm dotnet. Đặt biến môi trường
{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
4 thành vị trí cài đặt, cho phép
{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
2 cách tìm các tập hợp và các tệp hỗ trợ khác để lưu trữ. NET

import os
os.environ["DOTNET_ROOT"] = "/root/.dotnet"

Bây giờ chúng ta đã biết dotnet ở đâu, chúng ta cần tải CLR lên và thiết lập cấu hình thời gian chạy. Cấu hình thời gian chạy mô tả các khía cạnh khác nhau về cách chúng tôi sẽ chạy. Chúng ta có thể tạo một

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
6 rất đơn giản

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}

Sự kết hợp của

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
4 và cấu hình thời gian chạy cho phép tải CLR bằng các hàm
{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
8 và
{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
9

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)

Bây giờ chúng tôi đã tải CLR, chúng tôi cần tải công cụ PowerShell. Đây là một chút không rõ ràng. Ban đầu, tôi chỉ cố tải

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
0 nhưng không thành công do lỗi xác thực tên mạnh. Tuy nhiên, nếu tôi tải
# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
1 trước, tôi có thể tránh được lỗi đó. Tôi vẫn chưa chắc tại sao tôi cần tải assembly này trước, đó vẫn là điều tôi cần xác định

import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser

Cuối cùng, tôi muốn đặt các vị trí của dotnet và

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
2 có thể định cấu hình được, nhưng hiện tại, tôi có thứ mình cần

Giờ đây, công cụ PowerShell đã có sẵn cho tôi, tôi đã tạo một số hàm trợ giúp để xử lý kết quả dễ dàng hơn từ Python. Tôi cũng đã tạo một đối tượng PowerShell (_______10_______3) mà tôi sẽ sử dụng trong một số chức năng của mình

ps = PowerShell.Create()
def PsRunScript(script):
    ps.Commands.Clear()
    ps.Commands.AddScript(script)
    result = ps.Invoke()
    rlist = []
    for r in result:
        rlist.append(r)
    return rlist

class ParseResult:
    def __init__(self, scriptDefinition, tupleResult):
        self.ScriptDefinition = scriptDefinition
        self.Ast = tupleResult[0]
        self.Tokens = tupleResult[1]
        self.Errors = tupleResult[2]

    def PrintAst(self):
        print(self.ast.Extent.Text)

    def PrintErrors(self):
        for e in self.Errors:
            print(str(e))

    def PrintTokens(self):
        for t in self.Tokens:
            print(str(t))

    def FindAst(self, astname):
        Func = getattr(System, "Func`2")
        func = Func[System.Management.Automation.Language.Ast, bool](lambda a : type(a).__name__ == astname)
        asts = self.Ast.FindAll(func, True)
        return asts

def ParseScript(scriptDefinition):
    token = None
    error = None
    # this returns a tuple of ast, tokens, and errors rather than the c# out parameter
    ast = Parser.ParseInput(scriptDefinition, token, error)
    # ParseResult will bundle the 3 parts into something more easily consumed.
    pr = ParseResult(scriptDefinition, ast)
    return pr

def ParseFile(filePath):
    token = None
    error = None
    # this returns a tuple of ast, tokens, and errors rather than the c# out parameter
    ast = Parser.ParseFile(filePath, token, error)
    # ParseResult will bundle the 3 parts into something more easily consumed.
    pr = ParseResult(filePath, ast)
    return pr

def PrintResults(result):
    for r in result:
        print(r)

Tôi thực sự muốn bắt chước các phương thức PowerShell AST với một số hàm Python thân thiện hơn. Để tạo hàm FindAst(), tôi cần kết hợp ủy nhiệm trong c# với tính năng lambda trong Python. Thông thường, trong PowerShell, điều này sẽ giống như

$ast.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)

Nhưng tôi nghĩ từ một tập lệnh Python, việc sử dụng tên của loại sẽ dễ dàng hơn. Bạn vẫn cần biết tên của loại, nhưng bing là tuyệt vời cho loại điều đó. Như tôi đã nói, tôi thực sự không biết ngôn ngữ Python, vì vậy tôi hy vọng có nhiều cách tốt hơn để xử lý

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
4 mà
# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
5 trả về. Tôi thấy rằng tôi phải lặp lại kết quả cho dù thế nào đi chăng nữa, vì vậy tôi đã tích hợp nó vào chức năng tiện lợi. Bất cứ ai có đề xuất đều được chào đón để cải thiện điều này

Sự vinh quang

Bây giờ chúng ta đã có mô-đun cơ sở cùng nhau, chúng ta có thể viết một số Python khá đơn giản để thực thi các tập lệnh PowerShell của mình. Gọi tập lệnh PowerShell giờ đây dễ dàng như

#!/usr/bin/python3

from pspython import *

scriptDefinition = 'Get-ChildItem'
print(f"Run the script: '{scriptDefinition}")
result = PsRunScript(scriptDefinition)
PrintResults(result)
/root/__pycache__
/root/dotnet-install.sh
/root/get-pip.py
/root/grr.py
/root/hosted.runtimeconfig.json
/root/pspar.py
/root/pspython.py
/root/psrun.py

Bạn sẽ nhận thấy rằng đầu ra không được định dạng bởi PowerShell. Điều này là do Python chỉ lấy. NET và (về cơ bản) gọi

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
6 trên chúng

Cũng có thể truy xuất các đối tượng và sau đó quản lý định dạng qua PowerShell. Ví dụ này truy xuất các đối tượng qua

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
7, chọn những tệp bắt đầu bằng “ps” trong Python, sau đó tạo kết quả chuỗi ở định dạng bảng

scriptDefinition = 'Get-ChildItem'
result = list(filter(lambda r: r.BaseObject.Name.startswith('ps'), PsRunScript(scriptDefinition)))
ps.Commands.Clear()
ps.Commands.AddCommand("Out-String").AddParameter("Stream", True).AddParameter("InputObject", result)
strResult = ps.Invoke()
# print results
PrintResults(strResult)
    Directory: /root

UnixMode   User             Group                 LastWriteTime           Size Name
--------   ----             -----                 -------------           ---- ----
-rwxr-xr-x root             dialout             6/17/2022 01:30           1117 pspar.py
-rwxr-xr-x root             dialout             6/16/2022 18:55           2474 pspython.py
-rwxr-xr-x root             dialout             6/16/2022 21:43            684 psrun.py

Nhưng đó không phải là tất cả

Chúng ta cũng có thể gọi các phương thức tĩnh trên các loại PowerShell. Các bạn để ý trong module của mình có một số chức năng liên quan đến ngôn ngữ. Các hàm

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
8 và
# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
9 cho phép chúng tôi gọi trình phân tích cú pháp ngôn ngữ PowerShell để kích hoạt một số tình huống rất thú vị

Hãy tưởng tượng tôi muốn xác định những lệnh mà tập lệnh đang gọi. PowerShell AST làm cho điều đó trở nên dễ dàng, nhưng trước tiên chúng ta phải sử dụng trình phân tích cú pháp. Trong PowerShell, điều đó sẽ được thực hiện như thế này

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
0

AST kết quả được lưu trữ trong

import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
0, mã thông báo trong
import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
1 và lỗi trong
import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
2. Với mô-đun Python này, tôi gói gọn nó vào hàm Python
# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)
9, hàm này trả về một đối tượng chứa cả ba kết quả đó trong một phần tử. Tôi cũng đã tạo một vài hàm trợ giúp để in mã thông báo và lỗi dễ dàng hơn. Ngoài ra, tôi đã tạo một hàm cho phép tôi tìm bất kỳ loại AST nào (hoặc AST phụ) trong bất kỳ AST tùy ý nào

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
1

Lưu ý rằng có một kiểm tra cho commandName không phải là null. Điều này là do khi sử dụng

import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
4, tên lệnh không thể được xác định thông qua phân tích tĩnh do tên lệnh được xác định trong thời gian chạy

…một số, uh, điều kiện, uh, một vài pro quo

Trước tiên, bạn phải cài đặt dotnet (thông qua install-dotnet), cũng như cài đặt đầy đủ PowerShell.

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}
2 không chạy trên tất cả các phiên bản Python, tôi chỉ thử nghiệm nó trên Python 3. 8 và Trăn 3. 9 trên Ubuntu20. 04. Vào thời điểm tôi viết bài này, tôi không thể chạy nó trên Python 3. 10. Có thêm thông tin về pythonnet tại trang web pythonnet. Ngoài ra, đây là phiên bản lưu trữ của PowerShell. Một số thứ, chẳng hạn như tiến độ, dài dòng và lỗi có thể hoạt động hơi khác so với những gì bạn thấy từ
import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
6. Theo thời gian, tôi có thể sẽ thêm các hàm trợ giúp bổ sung để truy xuất thêm thông tin thời gian chạy từ phiên bản công cụ. Nếu bạn muốn tham gia, tôi rất sẵn lòng nhận Yêu cầu kéo hoặc hiểu đơn giản là các trường hợp sử dụng của bạn tích hợp PowerShell và Python

Lấy nó ra để quay

Tôi đã kết thúc tất cả những thứ này và thêm một Dockerfile (chạy trên Ubuntu 20. 04) trên github. Để tạo hình ảnh docker, chỉ cần chạy

import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser
7 từ thư mục gốc của kho lưu trữ

Bạn có thể sử dụng Python và PowerShell cùng nhau không?

Có thể chạy PowerShell từ bên trong python bằng quy trình con .

PowerShell có thể chạy tập lệnh không?

Bắt đầu trong Windows PowerShell 3. 0, bạn có thể sử dụng tính năng "Chạy bằng PowerShell" để chạy tập lệnh từ File Explorer trong Windows 8 và Windows Server 2012 cũng như từ Windows Explorer trong các phiên bản Windows cũ hơn.