Powershell7 研讨会(四)

原文:annas-archive.org/md5/a7c5d98eb6dd2beb9d7fc74516c7725b

译者:飞龙

协议:CC BY-NC-SA 4.0

第十四章:Linux 和 macOS 的 PowerShell 7

通常说,Linux 是最受欢迎的服务器操作系统,虽然这是真的,但这并没有说明几乎每台服务器都有几种不同类型的 Linux。与 Windows 和 macOS 的紧密工程化和维护不同,这些系统的公司致力于确保每个人都在运行同质、安全、并且经常强制更新的代码,而 Linux 世界则更加自由,拥有多种免费、开源且常常维护不足的变种,这些变种可能在服务器上存活多年甚至几十年。在本章中,我们将看看如何在一些常见的 Linux 版本上使用 PowerShell;在我们的例子中,使用 Ubuntu 和 CentOS,它们是免费的 Red Hat Enterprise Linux (RHEL) 版本。

我们将首先了解如何访问一台 Linux 机器进行练习;如果没有这个,本章内容会非常无聊。一旦我们有了机器,我们将探索三种不同的安装 PowerShell 的方法,使用包管理器和直接下载。

接下来,我们将安装 VS Code,并在 CentOS 7 上设置它,然后看看在 Linux 上运行 PowerShell 和在 Windows 上运行之间的主要区别。

我们将了解在 Linux 上使用 PowerShell 的最常见方法之一:通过 安全外壳协议 (SSH) 进行远程会话。这一点很重要,而且无疑是我看到人们登录 Linux 机器时最常用的方法。

最后,我们将快速了解如何使用免费开源包管理器 Homebrew 在 macOS 上轻松安装 PowerShell 和 VS Code。

本章将涵盖的主要主题如下:

  • 安装 PowerShell 7

  • 安装 VS Code

  • 在 Linux 上运行 PowerShell

  • 使用 OpenSSH 远程连接

  • macOS 上的 PowerShell

技术要求

除非我们已经有了运行某种 Linux 的客户端,否则我们需要一台 Linux 设备来工作。获取一台设备有两种方法,一种简单,另一种稍微困难一些。我在我的机器上做了这两种方法,获取了本章的截图,提供了一个没有图形界面的 Ubuntu 服务器和一个 CentOS 桌面客户端。

Ubuntu 服务器非常容易安装:

  1. 转到 控制面板 | 程序和功能 | 启用或关闭 Windows 功能,确保选中 Windows Subsystem for Linux(Windows 子系统 Linux)的复选框,像这样:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_1.jpg

图 14.1 – 启用 Windows 子系统 Linux

Windows Subsystem for Linux 在红色框中显示。

  1. 一旦启用了 Windows Subsystem for LinuxWSL),重启计算机,访问 Microsoft Store (apps.microsoft.com),并选择一个 Linux 应用。我使用的是最新版本的 Ubuntu – 22.04:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_2.jpg

图 14.2 – 在 Microsoft Store 中的 Ubuntu 应用

我在这里做的就是在搜索框中搜索 Ubuntu,并选择了最新的应用程序。

  1. 点击 安装,会弹出一个 Microsoft Store 窗口;点击 获取,下载应用程序后,按钮会变成 打开

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_3.jpg

图 14.3 – Microsoft Store 应用

  1. 点击正在安装,这可能需要几分钟...。几分钟后,我们会被要求创建用户名,然后输入两次密码,完成后就可以了。现在,我们已经在 Windows 上的 WSL 中运行了一个 Ubuntu 服务器:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_4.jpg

图 14.4 – Ubuntu 在 WSL 上运行

在第一行,我创建了一个新的用户名 nickp(富有创意吧),在第二行和第三行,我输入了新的密码。Ubuntu 在输入时隐藏密码,但与 Windows 不同,它不会通过显示点或星号来隐藏,而是完全不显示……什么都不显示。

就这样——我们的 Ubuntu 环境已经准备好安装 PowerShell。

注意

这些说明适用于 Windows 10 Pro 版本 19045。其他平台,如 Windows 11 或 Windows Server,可能有不同的安装 WSL 的说明。另外,这是 WSL,后来的操作系统还有一个版本叫做 WSL2。

我还在 Oracle VirtualBox 中安装了 CentOS;从 Microsoft Store 安装也完全可以,但我需要一台单独的机器,并且希望它有图形用户界面(GUI),我们可以在本章中使用。

安装 VirtualBox 的说明可以在这里找到:

www.virtualbox.org/manual/UserManual.html#installation

这非常简单。

一旦 VirtualBox 安装完成,我们需要从这里下载 CentOS 镜像:

isoredirect.centos.org/centos/7/isos

我们还需要创建一个虚拟机来安装它。这里有很好的说明:www.linuxfordevices.com/tutorials/centos/centos-on-virtualbox

我安装了带有 GNOME 桌面图形界面的 CentOS 7,这样我们就可以看看如何在较旧的操作系统上安装 PowerShell;我发现我使用的许多 Linux 系统都已经有些年头了。有些地方我仍在使用 Ubuntu 12.04。精确穿山甲(Precise Pangolin)是一个很棒的操作系统名称,但实际上,我们都应该使用 Jammy Jellyfish——至少为了安全起见。

最后,为了跟进 macOS 部分,我们需要一台 Mac。我是个极度节省的人,所以我借了朋友 Paul 的一台 MacBook,运行的是 OS 13 Ventura,这个版本相当新。谢谢你,Paul。

现在我们已经介绍了我们需要的环境类型以及如何安装它,让我们来看看如何安装 PowerShell。

安装 PowerShell 7

有些许反常的是,我发现将 PowerShell 安装在 Linux 上可能比在 Windows 上安装更容易,尽管 PowerShell 是为 Windows 开发的。微软发布了适用于支持版本 Linux 的好脚本;不过请注意,支持的 Linux 版本非常有限:RHEL、Ubuntu、Alpine 和 Debian。

这并不意味着我们不能在其他版本上安装 PowerShell,只是微软不提供任何支持。微软只支持特定的较新版本。而且,由于操作系统更新的速度较快,确保我们的 PowerShell 和 Linux 版本处于支持矩阵中是值得的;否则,我们可能会遇到意想不到的结果,正如我们在尝试在 CentOS 上安装时会看到的。

让我们从 Ubuntu 开始,我在我的 Windows 10 客户端中运行着 Ubuntu 的 WSL。

在 Ubuntu 22.04 上安装 PowerShell

在本节中,我们将安装 PowerShell 7.4 版本在 Ubuntu 22.04 上。Ubuntu 在我的 Windows 10 客户端中通过 WSL 运行,但在虚拟机或物理机器上同样适用。这里有详细的替代安装说明:learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu

让我们试试吧。我们将运行接下来的命令:

  1. 在第一行,我们正在从默认的软件库更新本地软件包至最新版本。在 Linux 上安装任何软件之前,这是一个好习惯。我们使用sudo命令告诉 Linux 我们希望使用管理员权限或根权限来执行该命令。显然,要做到这一点,我们需要拥有这些权限,并且在运行命令之前,系统会要求我们输入账户密码以确认:

    sudo apt-get update
    
  2. 在第二行,我们正在安装一些前置的软件包:

    sudo apt-get install -y wget apt-transport-https software-properties-common
    
  3. 在第三行,我们正在获取操作系统的确切版本,然后在第四行使用它:

    wget to download the correct repository package for our operating system. This is the Linux package repository for Microsoft products, abbreviated PMC (short for packages.microsoft.com):
    
    

    dpkg,Ubuntu 的软件包管理系统:

    sudo dpkg -i packages-microsoft-prod.deb
    
    
    
  4. 在第六行,我们正在删除密钥文件以确保安全:

    sudo apt-get update again to make sure we’ve got the latest list of packages for the new repository:
    
    

    sudo apt-get update

    
    
  5. 最后,在第八行,我们正在安装 PowerShell:

    sudo apt-get install -y powershell
    

Linux 输出非常详细,我们在输入上述命令时会看到 60 到 70 行输出,但整个过程非常直接,且运行良好。

要启动 PowerShell,我们只需输入pwsh,即可进入 PowerShell 提示符,如下图所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_5.jpg

图 14.5 – 在 Ubuntu 上启动 PowerShell

在前面的截图中,我用 pwsh 启动了 PowerShell。一旦 PowerShell 启动,我调用了 $PSVersionTable 自动变量来获取有关环境的一些信息,包括 PowerShell 的版本、版本号以及运行的操作系统。我通过输入 exit 关闭了 PowerShell。如果我想让 PowerShell 在后台运行,我可以输入 bash,然后切换到 Bash 提示符。正如我们所看到的,在 Linux 上,我们获得了与 Windows 相同的颜色编码,这有助于跨平台保持一致的体验。

了解 Linux 中也支持制表符补全是很好的。Linux 中最常让我困惑的事情之一就是大小写问题;Windows 上的 PowerShell 已经让我变得懒惰。因此,能够使用制表符来完成路径非常有用。请注意,PowerShell 在 Linux 上对大小写不敏感;get-processGet-Process 作用一样。

现在让我们来看一下如何在 CentOS 上安装 PowerShell。

在 CentOS 8 和 9 上安装 PowerShell

在 CentOS 的最新版本(CentOS 8 或 CentOS 9)上安装 PowerShell 的过程与在 Ubuntu 上安装非常相似,只是使用了 RHEL 8 的 yum 包管理器,或使用 RHEL 9 的 dnf 包管理器;如果说有区别的话,它甚至更简单。不过需要注意的是,微软官方只在 RHEL 上支持 PowerShell,而不是 CentOS 或 Fedora。我们接下来要运行的是以下命令:

  1. 在第一行,我们使用 curl 应用程序而不是 wget 来获取 PCM:

    rpm, the package manager:
    
    

    sudo rpm -i packages-microsoft-prod.rpm

    
    
  2. 在第三行,我们再次移除密钥文件:

    rm packages-microsoft-prod.rpm
    
  3. 在第四行,我们正在更新软件包列表,现在我们已经注册了新的仓库:

    -y switch at the end specifies that we are answering yes to all questions. We can start PowerShell using pwsh, again, just like on Ubuntu:
    
    

    sudo dnf install powershell -y

    
    

故障排除提示

如果这不起作用,很有可能是代理设置的问题。在 CentOS 中,至少有三个不同的地方可能包含代理设置。对于 yum,它在 /etc/yum.conf。对于 dnf,它在 /etc/dnf/dnf.conf

那么,如果我们想在旧版本上使用 PowerShell 怎么办?我们必须安装一个特定的旧版本 PowerShell。

对于 RHEL 7(因此,也包括 CentOS 7),PowerShell 的最后一个可用版本是 7.2. 7.3 的早期版本可以使用,但后续版本不行,因为它们依赖于与 CentOS 7 不兼容的 Linux 库。如果我们尝试安装 PowerShell 的新版本,我们会看到类似这样的错误信息:

GLIBCXX_3.4.21 not found (required by pwsh)

这里的答案是安装 PowerShell 7.2. 让我们看看如何做到这一点。

在 CentOS 7 上安装 PowerShell

在 CentOS 上通过直接下载安装非常简单;只需要一行命令:

sudo yum install https://github.com/PowerShell/PowerShell/releases/download/v7.2.17/powershell-7.2.17-1.rh.x86_64.rpm

我们在这里所做的就是使用yum,CentOS 7 上的包管理器,从 URL 获取并安装一个包。这里的技巧是知道你需要下载的包的 URL。所有这些包都由 Microsoft 在 GitHub 上维护,网址是:github.com/PowerShell/PowerShell/releases/

要下载版本,点击软件包名称中的链接(在我们的例子中是powershell-7.2.17-1.rh.x86_64.rpm),并复制超链接:github.com/PowerShell/PowerShell/releases/download/v7.2.17/powershell-7.2.17-1.rh.x86_64.rpm

一旦我们有了它,就可以像这样将其传递给sudo yum install

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_6.jpg

图 14.6 - 在 CentOS 7 上使用直接下载安装 PowerShell

在第一行中,我运行了之前展示过的直接下载命令。其余的屏幕输出是操作系统的聊天信息,告诉我们它正在做什么。在我们最终运行 PowerShell 之前,还有很多行这样的输出。这是安装 PowerShell 的一个非常简单的方法;缺点是我们没有注册 Microsoft 的仓库。

这基本上就是我们在 Linux 上安装 PowerShell 时要涵盖的内容;我们已经涵盖了两个主要 Linux 发行版的代表,Ubuntu 和 CentOS,并且我们已经了解了如何安装不同版本的 PowerShell。现在让我们来看看如何在 Linux 上安装 VS Code。我们将使用我的 CentOS 系统,因为它有图形界面。

安装 VS Code

在 Linux 上安装 VS Code 是简单的。在最近的 Ubuntu 机器上,我们可以像这样使用snapd,一个 Ubuntu 的包管理系统:

sudo snap install --classic code

就这样。在 RHEL 和 CentOS 机器上,我们可能需要先启用snapd,然后才能使用它来安装 VS Code。

我们将运行接下来的命令:

  1. 在第一行中,我们正在安装snapd包:

    snap uses:
    
    

    /snap 到 /var/lib/snapd/snap:

    sudo ln -s /var/lib/snapd/snap /snap
    
    
    
  2. 最后,我们需要注销并重新登录,或者重启计算机,以确保所有内容都已更新。

现在,我们准备像之前一样使用snap来安装 VS Code。使用snap的最大优势是它会在后台保持 VS Code 的更新。

如果我们无法使用snap,也可以手动安装 VS Code。在 CentOS 7 上,我们可以这样做:

sudo rpm --import  https://packages.microsoft.com/keys/microsoft.asc

这将使用rpm,CentOS 7 的包管理器,注册 Microsoft 的 GPG 加密密钥。接下来,键入以下内容:

sudo nano /etc/yum.repos.d/vscode.repo

这将创建一个名为vscode.repo的空文本文件。我们需要向该文件中添加一些行并保存,因此请键入以下内容:

[code]
name=Visual Studio Code
baseurl=https://packages.microsoft.com/yumrepos/vscode
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc

CtrlX退出,并在提示保存文件时按y

vscode.repo文件应该是这样的:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_7.jpg

图 14.7 - 使用 nano 查看 vscode.repo 文件

当我们创建文件时,它是空的,我们必须输入代码并保存退出。

最后,安装 VS Code,请输入以下命令:

sudo yum install code

就这样。我们可以通过在终端提示符下输入code来启动 VS Code。VS Code 在 Linux 和 Windows 上的工作方式完全相同。请参阅 第五章配置 VS Code 用于 PowerShell 部分,PowerShell 控制流 – 条件语句 和循环

现在我们已经安装好了一切,让我们看看如何在 Linux 上使用 PowerShell。

在 Linux 上运行 PowerShell

通常,PowerShell 在 Linux 上的工作方式与 Windows 上完全相同,但显然,两种操作系统之间存在一些差异,我们需要注意并理解 PowerShell 如何处理这些差异。

大小写敏感是显而易见的区别;虽然在 Linux 上 get-contentGet-Content 是等效的,但如果文件名是 MyFile.txt,则 get-content ./myfile.txt 不会生效;请看下面的截图:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_8.jpg

图 14.8 – 大小写的重要性

如你所见,如果路径或文件名的大小写不正确,PowerShell 将无法找到该文件。我找到的最佳解决方法是尽可能使用 Tab 完成,因为 Tab 完成忽略大小写,所以输入 myfi 并按下 Tab 键会找到名为 MyFile.txt 的文件。

文件系统也不同。Linux 不使用字母区分驱动器,而是使用正斜杠 (/),而不是反斜杠 (\)。PowerShell 识别两者作为文件路径分隔符,因此 Get-Content ./MyFile.txtGet-Content .\MyFile.txt 没有功能上的区别:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_9.jpg

图 14.9 – 多功能路径分隔符

如你所见,无论我们选择哪种文件路径分隔符,都能获取到文件的内容。这使得编写跨平台脚本变得更容易。

我已经表明了我对别名的看法,看来我的愤怒信件写作有了回报,因为 PowerShell 7 在 Linux 上不再包含像 ls 这样的常用别名,尽管它们在 Windows 上运行 PowerShell 7 时仍然存在。相反,PowerShell 现在调用 Bash 命令,像这样:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_10.jpg

图 14.10 – PowerShell 7 在 Linux 上的别名减少

在前面的截图中,我们可以看到在 Bash 和 PowerShell 中运行 ls 的区别。输出是相同的,但我们在 Bash 中运行时没有颜色编码。相比之下,当我们运行 Get-ChildItem 时,输出完全不同。与 Windows 上的行为对比,ls 在 Windows 上是 Get-ChildItem 的别名。其他不再有别名的 Linux 命令包括 cpmvrmcatmanmountps

在 Linux 上以管理员身份运行也有所不同。熟悉 Linux 的人通常习惯使用 sudo 前缀来以 root 用户身份运行命令。这在 PowerShell 中不起作用。相反,我们必须以 sudo 启动 PowerShell,像这样:

sudo pwsh

这将为我们提供一个具有 root 权限的新 PowerShell 会话。

鉴于在 Linux(以及 macOS 和 ARM)上运行 PowerShell 的一个主要吸引力是我们可以开始编写跨平台脚本,那么如何知道我们的脚本在哪个平台上运行呢?很简单——我们测试自动变量。有三个自动变量,分别是 $IsWindows$IsLinux$IsMacOS,根据操作系统的不同,这些变量会返回 truefalse。我们可以使用这些变量在脚本中编写 if 语句,以根据环境改变行为。

活动 —— 编写跨平台脚本

根据我们之前学到的内容,编写一个跨平台脚本,能够在 Windows 和 Linux 上运行,并返回当前 CPU 使用率最高的五个进程。将进程按降序输出到一个文本文件中,文件名应包含运行该脚本的计算机名称。

我们可以在 Windows 中使用 $env:computername 获取计算机名称,而在 Linux 中可以通过输入 hostname 来获取。

当然,大多数时候,我们不会直接在 Linux 机器上运行命令和脚本;大多数时候,我们会想要远程连接到它。在接下来的部分中,我们将看看远程连接到 Linux 机器的推荐方法。

使用 OpenSSH 进行远程连接

第十二章《确保 PowerShell 安全》一节中,我们看到远程连接是一种强大的方式,可以与机器建立连接并进行控制。当我们在该章节中探讨远程连接时,我们研究了在其他 Windows 机器上通过Windows 远程管理WinRM)协议进行远程连接。我们还提到过可以使用 SSH 来建立远程会话。由于 Linux 不支持 WinRM 协议,因此我们必须使用 SSH 来远程管理它。

OpenSSH 是一个开源的 SSH 工具集,在 Linux 和其他 Unix 系统上几乎是无处不在的。从 2018 年起,它也可以在 Windows 上使用,使得管理异构环境变得更加容易。设置它可能有些复杂,但一旦配置完成,它会让远程连接变得非常简单。让我们来看看。

检查 PowerShell 是否支持 OpenSSH

首先要检查的是,我们的 PowerShell 7 版本是否支持 OpenSSH;如果我们从 GitHub 下载并安装了它,那么应该没问题,但首先让我们使用以下命令进行检查:

(Get-Command New-PSSession).ParameterSets.Name

如果我们看到名为 SSHHostSSHHostHashParam 的参数集,那么我们就可以继续了。如果没有,则应从 GitHub 下载最新版本的 PowerShell 7。

在 Windows 上安装 OpenSSH

我们只需要在 Windows 上安装 OpenSSH,前提是我们想要远程连接到这台 Windows 机器。如果我们要从这台机器远程连接到其他机器,则可以跳过安装它;PowerShell 已经内置了一个可以使用 PowerShell 远程连接 Linux 机器的 SSH 客户端。

如果我们决定要安装 OpenSSH 服务器,那么首先需要检查我们是否正在运行一个可行版本的 Windows,并且我们是否拥有正确的权限。启动一个提升权限的管理员 PowerShell 会话,然后输入以下内容:

(New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
Winver.exe

这是我在机器上得到的结果:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_11.jpg

图 14.11 – 检查 Windows 上 OpenSSH 的前提条件

正如我们所见,我的权限测试返回了True,说明我确实拥有正确的权限,当我运行winver.exe时,弹出窗口显示我正在运行 Windows 10 22H2 版本,比最低要求的 Windows 10 1809 版本还要新。我们已经准备好安装了。在刚才使用的提升权限提示符中,输入以下内容:

Get-WindowsCapability -Online | Where-Object {$_.Name -like 'OpenSSH*'} | Add-WindowsCapability -Online

这将安装 OpenSSH。我们还需要启动sshd服务,并将其设置为自动启动:

Start-Service sshd
Set-Service sshd -StartupType Automatic

最后,我们需要配置sshd以允许 PowerShell 使用它。以管理员身份打开notepad.exe(右键点击并选择C:\ProgramData\ssh\sshd_config文件,在文件的最后一行之前添加以下行,并保存文件:

PasswordAuthentication yes
PubkeyAuthentication yes
Subsystem powershell c:/progra~1/powershell/7/pwsh.exe -sshs -nologo

如果我们使用的是 PowerShell 7.4 或更高版本,则-``nologo参数不再需要。小心不要将文件保存为sshd_config.txt

警告!

等等!那条c:/progra~1/powershell/7/pwsh.exe路径是怎么回事?在 Windows 上使用非微软的开源软件有时会令人沮丧。OpenSSH 就是一个例子。它无法理解带有空格的路径,即使路径被单引号或双引号括起来,因此我们不得不使用一种叫做 8.3 格式的东西,这是一种旧版微软操作系统中使用的短文件名格式。

不幸的是,情况更复杂。一些版本的 OpenSSH 也不喜欢这种格式;当我们将 PowerShell 子系统添加到sshd_config文件中时,sshd服务拒绝启动。这里的解决办法是将另一个 PowerShell 副本侧载到一个没有空格、且文件夹名不超过八个字符的目录中。为了让它正常工作,我从 PowerShell GitHub 页面下载了 PowerShell 7.4 的 ZIP 文件,通过右键点击下载的文件并选择c:\scratch\pwsh来解锁它。然后,我将以下行添加到sshd_config中:

Subsystem powershell c:/scratch/pwsh/pwsh.exe -sshs

现在,sshd服务将正常启动。

最后,使用以下命令重新启动服务:

Restart-Service sshd

就这样。我们的 Windows 机器已经准备好通过 OpenSSH 接收 PowerShell 远程连接。

在 Linux 上安装 OpenSSH

现在,我们需要在 Linux 上配置 OpenSSH。我们将使用我的 CentOS 7 机器,它已经安装了 OpenSSH,但如果我们想在 Ubuntu 机器上安装它,首先需要使用以下命令安装:

sudo apt install openssh-client
sudo apt install openssh-server

一旦安装了 OpenSSH,我们需要编辑/etc/ssh下的sshd_config文件。为此,我们需要使用sudo启动文本编辑器:

sudo nano /etc/ssh/sshd_config

我们需要添加以下几行:

PasswordAuthentication yes
PubkeyAuthentication yes
Subsystem powershell /usr/bin/pwsh -sshs -nologo

然后,我们保存文件。接下来,我们需要重新启动sshd服务:

sudo systemctl restart sshd

然后,我们设置它自动启动:

sudo systemctl enable sshd

这就是我们设置远程连接的方式。请注意,如果最后的命令已经启用,可能会抛出错误。

运行远程会话

通过 SSH 使用远程会话和使用 WinRM 一样简单,我们在第十二章中看到过,Securing PowerShell。我们首先创建一个会话对象:

$session = New-PSSession -HostName <name of remote computer> -UserName <username>

请注意,我们使用的是-HostName参数,而不是-ComputerName参数。这告诉 PowerShell 创建一个 SSH 会话,而不是 WinRM 会话。系统会要求我们输入用户的密码,然后创建会话对象。然后我们可以使用包含会话对象的变量来启动远程会话:

Enter-PSSession -Session $session

我们会看到提示符变化,反映我们正在远程连接的机器,并且直接进入该机器上的 PowerShell 会话。要离开会话,我们只需输入exit并返回本地机器。这是实际操作的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_12.jpg

图 14.12 – 通过 SSH 远程连接到 Linux 服务器

上述截图中的编号解释如下:

  1. 第 1 行,我创建了一个新的会话对象并将其存储在变量中。我传递了远程机器的 IP 地址,因为我没有设置名称解析。我还传递了一个具有登录权限的远程机器用户的用户名。

  2. 第 2 行,系统会要求输入远程用户的密码;一旦密码输入,会创建会话对象。

  3. 第 3 行,我调用了$session变量,显示了新会话的属性。

  4. 第 4 行,我将$session变量传递给Enter-PSSession,并进入了远程会话。

  5. 第 5 行,我们可以看到提示符已经变更为[nick@192.168.56.101]: PS /home/nick>,这告诉我们我正在192.168.56.101的机器上进行 PowerShell 会话,并且登录用户名是nick。工作目录是/home/nick。我正在运行Get-Process PowerShell cmdlet,查找包含shell字符串的进程,返回了两个gnome进程。这显然是 Linux——更准确地说,是我的 CentOS 7 图形界面系统。如果我们在 Ubuntu 上运行,可能根本看不到任何进程,前提是没有安装图形界面。

  6. 第 6 行,我运行了hostname Bash 命令,返回了远程系统的名称:localhost.localdomain

  7. 第 7 行,为了避免疑惑,我们可以看到$IsLinux自动变量的值是True

  8. 最后,在第 8 行,我输入exit并返回到我本地运行在 Windows 上的 PowerShell 会话。

问题是,我在这里使用了用户名和密码组合,而许多 Linux 机器将被设置为使用基于密钥的身份验证。让我们来看看如何设置它。

身份验证

基于密钥的身份验证是一种更安全的 SSH 远程访问方式。它还使得自动化脚本更加简单,因为一旦设置完成,就不需要手动输入密码。让我们看看如何使它正常工作。

PowerShell 7 包括一个名为Ssh-keygen的工具,我们可以使用它来创建公钥/私钥对,进而用来认证我们自己到远程机器。我们可以这样使用它:

Ssh-keygen -t Ed25519

我们正在要求 PowerShell 使用 Ed21559 算法生成一个密钥对,这是相当现代的算法。较旧的系统可能需要我们改用 RSA 算法。系统会要求我们提供保存文件的路径,最好接受默认路径,只需按Enter即可。系统还会要求输入密码短语;同样,这个步骤是可选的,我们可以按Enter两次以保存没有密码短语的文件。

现在,我们可以将公钥保存到 Linux 机器的.ssh目录中,保存路径是我们想要登录的用户目录。PowerShell 还有一个叫做scp的工具,我们可以用它来复制文件(注意,可能需要先创建一个.ssh目录):

scp c:\Users\<username>\.ssh\id_ed25519.pub <user>@<remote_host>:~/.ssh/authorized_keys

我在这里使用scp传递两个参数——第一个是我们创建的文件路径,第二个是我们希望将文件复制到的路径。我们会再次被要求提供远程用户的密码,但这是我们最后一次需要这样做。现在,当我们以该用户身份登录时,我们将传递本地机器上私钥的哈希值,这个哈希与远程机器上公钥的哈希值配对,我们就会被识别为远程用户。这就是它在我的机器上的表现:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_13.jpg

图 14.13 – 设置基于密钥的身份验证

在前面的截图中,我已经运行了设置基于密钥身份验证所需的命令:

  1. 第 1 行,我运行Ssh-keygen来创建我的密钥对。

  2. 第 2 行,我通过按Enter接受默认路径。

  3. 第 3 行,我通过按Enter两次设置了一个空的密码短语。

  4. 第 4 行,我使用scp将公钥复制到远程机器上用户的.ssh目录中。

  5. 第 5 行,我提供了密码——希望这是最后一次提供密码。

  6. 第 6 行,我将一个新的会话对象存储在名为$sessionSSH的变量中。请注意,我没有提供密码,而是提供了-``KeyFilePath参数中的私钥路径。

  7. 第 7 行,我正在调用变量以检查其属性。

  8. 第 8 行,我正在使用$``sessionSSH变量进入会话。

  9. 在最后一行,我们可以看到提示符已经改变,表明我正在远程会话中工作。

使用 SSH 还有很多要学的内容,但这些已经足够让我们入门。接下来,我们进入本章的最后部分:macOS 上的 PowerShell

macOS 上的 PowerShell

macOS 与 Linux 非常相似;这两个操作系统都基于 Unix 元素,并且许多 Linux 程序可以在 macOS 上运行,无需修改源代码。我们将关注的差异在于如何安装 PowerShell 和 VS Code。我使用了一个朋友的 MacBook,它运行的是 Ventura(macOS 13)。如果有什么不同的话,在 macOS 上安装比在 Linux 上更简单。

在 macOS 上安装 Homebrew

Homebrew 是一个免费的开源包管理器,适用于 Linux 和 macOS,但我们大多在 macOS 上看到它。它非常容易安装和使用,正是我们用来在 macOS 上安装 PowerShell 和 VS Code 的工具。它是通过一行命令安装的。打开终端并输入以下命令:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

我们将 curl 命令传递给 bash shell,从一个 URL 下载并运行一个 Bash 脚本。可能会要求我们提供密码。macOS 和 Linux 一样健谈,但几分钟后,我们应该会看到一条消息,显示 安装成功!。现在,我们可以安装 PowerShell 了。

在 macOS 上安装 PowerShell

一旦安装了 Homebrew,其他一切都很简单。要安装 PowerShell,我们输入以下命令:

brew install powershell/tap/powershell

就这样。我们可以通过在终端中输入 pwsh 启动 PowerShell。这是 Paul 机器上的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_14_14.jpg

图 14.14 – 在 macOS 上安装 PowerShell

第 1 行 中,我使用 Homebrew 安装 PowerShell。在 第 2 行 中,我使用 pwsh 启动 PowerShell,在 第 3 行 中,我调用 $PSVersionTable 来检查我们安装了什么版本。

我们需要保持 PowerShell 更新。我们可以通过这两行命令来实现:

brew update
brew upgrade powershell

第一行更新 Homebrew 数据库,第二行根据最新信息升级 PowerShell。

最后,要卸载 PowerShell,我们只需输入以下命令:

brew uninstall --cask powershell

然而,我不知道为什么我们会想这样做。接下来我们来看如何安装 VS Code。

在 macOS 上安装 VS Code

我们可以使用 Homebrew 下载并安装 VS Code,使用以下命令,这会更新 Homebrew 的最新文件:

brew update

如果 cask 仓库尚未存在,这将安装该仓库:

brew tap caskroom/cask

这会在仓库中搜索 VS Code:

brew cask search visual-studio-code

这行命令安装它:

brew cask install visual-studio-code

我们可以使用启动器应用程序启动 VS Code,或者也可以通过以下 cat 命令将其添加到 PATH 变量中:

cat << EOF >> ~/.bash_profile
# Add Visual Studio Code (code)
export PATH="\$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin"
EOF

我们也可以通过直接从 code.visualstudio.com/ 下载并双击下载的文件来安装它。

我们可以通过运行以下命令卸载 VS Code:

brew cask uninstall visual-studio-code

但我敢打赌我们不想这么做。

本章到此为止。让我们回顾一下所学的内容。

总结

在本章中,我们看到安装 PowerShell 和 VS Code 没有标准的方式。我们展示了如何使用包管理器在 Ubuntu 和 RHEL 上安装 PowerShell 7,然后使用直接下载的方法在较旧版本的 Linux(CentOS 7)上进行安装。

之后,我们查看了如何在 Linux 上安装 VS Code,并且在 CentOS 7 上进行了实际操作演示。

我们查看了 PowerShell 在 Linux 上与 Windows 上运行的不同之处,包括文件系统、大小写和别名的使用。

我们花了一些时间讨论了一个重要话题:如何通过 SSH 远程连接到 Linux 机器。Linux 作为桌面操作系统的使用较为罕见,大多数在 Linux 机器上的工作都是通过 SSH 远程会话进行的,无论是使用 PowerShell 还是直接进入 Bash 终端。

最后,我们快速浏览了在 macOS 上安装 PowerShell 和 VS Code 的几种方法。我们看到,通过免费的开源包管理器 Homebrew 安装和卸载这些应用程序非常容易。

在下一章,我们将介绍如何在不同的处理器 ARM 和相关的操作系统 Raspbian 上运行 PowerShell。

练习

  1. 如果我们需要在 Kali Linux 上安装 PowerShell,应该去哪里寻求支持?

  2. 当我们在 Linux 上的 PowerShell 会话中输入 ls 时,调用的是哪个命令?

  3. PowerShell 在 Linux 上使用哪个文件路径分隔符?

  4. 我们如何轻松判断是否在 macOS 机器上工作?

  5. 我们如何在 Linux 中以 root 权限运行 PowerShell 脚本?

  6. 我们应该使用哪个 cmdlet 和参数来创建到 Linux 机器的远程会话?

  7. 我们应该使用哪个 cmdlet 和参数来避免通过网络发送密码?

  8. scp 做什么?

  9. 什么是 Ed25519?

进一步阅读

第十五章:PowerShell 7 和 Raspberry Pi

这是我最期待写的章节之一。我家里有一堆 Raspberry Pi(还有 Arduino、micro:bit 和 ESP32 等)。我用它们来教孩子们(和成年人)编程,还用它们运行广告拦截器、媒体中心、野生动物监控摄像头和园艺系统。我有一个 Raspberry Pi,配有按钮,能够随机选择我最喜欢的广播节目并播放(叫做 Shendomizer)。大多数时候,我用 Python 来编程,但 PowerShell 也是一个选择。我们甚至可以在它们上安装简化版的 Windows 10,不过这本书不会讲到这个。相反,我们将讨论如何安装 PowerShell 7 和 Visual Studio (VS) Code,如何使用 PowerShell 和 VS Code 通过 SSH 远程连接到没有显示器的 Pi,以及 Raspberry Pi 上与 PowerShell 配合使用的默认模块,最后创建一个脚本,完成物理计算的第一步:让 LED 灯闪烁。

本章我们将涵盖的主要内容如下:

  • Raspberry Pi 简介

  • 安装 PowerShell 和 VS Code

  • 远程连接到 Pi

  • 在 Raspberry Pi OS 上运行 PowerShell

  • 简单的物理计算

技术要求

对于本章内容,我们需要一台 Raspberry Pi、一个电源、屏幕、键盘和鼠标,以及所需的电缆。本章的部分内容是在 Pi 400 上编写的,Pi 400 是一款将 Raspberry Pi 安装在键盘上的方便版本,除了显示器外,其他所需配件都已经包含。其他部分则是使用 Raspberry Pi 3 单板计算机编写的。

请注意,Pi Zero 或 Pi Pico 无法使用。它们使用不同版本的 ARM 芯片,因此架构无法与 .NET 配合使用。

对于简单的自动化,我们需要以下设备:

  • 一个面包板

  • 一个 LED

  • 一个 300-400 欧姆的电阻(但 250-500 欧姆的也可以)

  • 两根公对母跳线

Raspberry Pi 简介

Raspberry Pi 是一款小型、价格实惠且多功能的单板计算机,由英国的 Raspberry Pi 基金会开发。它的主要目标是促进在学校和发展中国家教授基础计算机科学。然而,由于其可获得性、低成本和易用性,使其在爱好者、教育工作者和专业人士中非常流行,用途包括从学习编程到构建复杂的项目。

它被设计成一个空白的起点,让用户理解硬件和软件之间的基本交互。这个计算机本质上是一个小型、自包含的 PC,可以用来完成许多与台式机或笔记本电脑类似的任务,比如浏览网页、文字处理和玩游戏。此外,它的 通用输入输出 (GPIO) 引脚使其能够与外部硬件互动,非常适合用于电子项目和 物联网 (IoT) 应用。

多年来,树莓派已经发布了多个型号,每个型号都对前一代进行了改进。最新的型号是树莓派 4 Model B。该型号配备了 64 位四核 ARM Cortex-A72 CPU,处理速度可达 1.5 GHz。它有 2 GB、4 GB 或 8 GB 的 LPDDR4-3200 SDRAM 可选。为了连接,它支持千兆以太网、蓝牙 5.0 和双频 Wi-Fi(2.4 GHz 和 5.0 GHz)。此外,它还配备了两个 USB 3.0 端口、两个 USB 2.0 端口、两个支持最高 4K 分辨率的 micro HDMI 端口,以及一个用于电源的 USB-C 端口。

树莓派可以运行各种操作系统OS),其中树莓派操作系统(以前称为 Raspbian)是最受欢迎的。这个基于 Debian 的操作系统针对树莓派硬件进行了优化,并预装了必要的工具、编程语言和应用程序。用户还可以安装不同版本的 Linux,甚至是 Windows 10 IoT Core 版本。操作系统可以通过将镜像烧录到 MicroSD 卡来安装。

在编程方面,Python 是最常用的语言,因为它简洁且功能强大。然而,树莓派支持众多其他语言,如 JavaScript、PHP、C++、Java,最重要的是,PowerShell 7。它的多功能性使其成为软件开发中的一项有价值的工具,尤其是在物联网和嵌入式系统中。

树莓派的应用范围非常广泛。在教育领域,它被用来教授编程、计算机科学基础,甚至硬件设计。爱好者用它来制作复古游戏主机、媒体中心和智能家居系统。在专业领域,它作为一种经济实惠的工具,用于原型制作、数据收集和自动化。我们也可以用它进行并行计算;例如,英国 GCHQ 运行的 OctaPi 项目就是一个例子。一些机构甚至将它用于超级计算机;洛斯阿拉莫斯国家实验室就用树莓派搭建了一个 750 节点的高性能计算机。

自 2012 年发布以来,树莓派生态系统不断扩展。树莓派有三种独立的系列:Pi 系列、Pi Zero 系列,后者作为轻量级且更便宜的替代品,适用于专门的项目,并可以持续运行(我有几台使用可充电电池运行的 Pi Zero 野生动物相机),以及 Pi Pico 系列,作为流行的 Arduino 单板计算机的替代品。PowerShell 7 仅能在完整版本的 Pi 上运行,不能在 Pi Zero 或 Pico 上运行。这些更小的替代品运行在 ARMv6 架构的芯片上;而 .NET,因此 PowerShell 7,需要 ARMv7 或 ARMv8 架构的芯片,正如树莓派 2、3 和 4 所使用的那样。这部分是因为 ARMv6 功耗较低,但也因为没有人愿意为老旧硬件设计,不是吗?

好的,我们开始在 Pi 上安装 PowerShell 吧?

安装 PowerShell 7 和 VS Code

Raspberry Pi OS 是基于 Debian 的 Linux 发行版,和 Ubuntu 一样,所以我们在 第十四章 中跟随的 Ubuntu 指令,PowerShell 7 for Linux 和 macOS,也适用,但有一种更简单的方法。

安装 PowerShell

如果我们访问 PowerShell 在 Linux 上的社区支持 页面 learn.microsoft.com/en-us/powershell/scripting/install/community-support,我们会找到一个非常方便的脚本,它将为我们安装 PowerShell,如下截图所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_1.jpg

图 15.1 – Raspberry Pi OS 安装脚本

如果我们点击脚本框右上角的 复制 按钮,我们可以简单地在 Raspberry Pi 上打开终端窗口,右键粘贴到终端中,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_2.jpg

图 15.2 – 通过将脚本粘贴到终端安装 PowerShell

粘贴完脚本后,我们只需按下前面截图中高亮显示的那一行上的 Enter 键,脚本就会运行,安装并启动 PowerShell。

当然,这可能容易出错,所以我们可能希望实际创建一个脚本,检查它是否正确,然后执行它。为此,从终端提示符输入以下命令:

nano

这将打开 nano 文本编辑器。将脚本粘贴到 nano 中,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_3.jpg

图 15.3 – 在 nano 中创建 PowerShell 安装脚本

检查你是否已正确粘贴脚本,然后按 Ctrl + X 保存,系统提示时输入 Y 以保存文件,并为文件命名——我将我的命名为 installPosh.sh。返回到终端提示符后,我们可以通过输入以下命令来运行脚本:

sudo bash ./installPosh.sh

再次,我们将直接进入 PowerShell。现在,让我们来看看安装 VS Code。

安装 VS Code

将 VS Code 安装到我们的 Pi 上更容易。VS Code 已包含在 Raspberry Pi OS 的官方软件库中,所以我们不需要手动下载文件或设置替代软件库——我们只需打开终端并输入以下命令:

sudo apt-get update
sudo apt install code

完成后,几分钟内,VS Code 将出现在我们的机器上,并伴随许多信息:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_4.jpg

图 15.4 – 在 Raspberry Pi 上安装并启动 VS Code

我们可以在终端中输入 code,或者通过点击左上角的树莓图标,进入 编程 子菜单,从应用程序菜单中找到它。

现在,让我们来看一下我最常用的 Raspberry Pi 使用方式:远程连接。

远程连接到 Pi

虽然在教育环境中,Raspberry Pi 被用作个人电脑是很常见的,但更有可能的是我们想把它作为某种服务器使用,因此我们会希望远程连接,而不是为其配置独立的显示器、鼠标和键盘。这被称为无头模式,接下来我们将讨论这个模式。

使用无头模式的 Pi

要使用无头模式的 Pi,我们需要设置它,使其能够连接到网络并远程访问。我们将配置一个新的 Pi(或者重新构建旧的 Pi),使其能够访问无线网络,并使用 SSH 进行访问,这在第十四章中已经提到过,Linux 和 macOS 上的 PowerShell 7。我们可以通过 Raspberry Pi 网站上的 Raspberry Pi Imager 工具来设置这两项功能,网址是 www.raspberrypi.com/software/。需要注意的是,许多网上文档建议我们可能需要创建并编辑一个名为 wpa_supplicant.conf 的文件。这对老版本的 Raspberry Pi OS 和 Raspbian 是适用的,但最新版本不会使用它。

下载适合我们将要运行操作系统的安装程序版本——在我的情况下是 Windows——并进行安装。

当我们运行它时,如果没有保持图形驱动程序更新,可能会遇到 OpenGL 错误,因此请确保安装了最新的驱动程序。

我们还需要一张准备好进行镜像的 microSD 卡。

当我们打开镜像工具时,会询问我们想安装在哪个设备上,想要什么操作系统,以及使用什么存储设备,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_5.jpg

图 15.5 – Raspberry Pi Imager 工具

在我的情况下,我正在安装在 Raspberry Pi 4 上,我想要最新的 64 位操作系统,并且我希望将镜像写入我笔记本电脑上的 SDHC 卡。当我们点击下一步时,会询问是否要应用操作系统定制设置。是的,我们要。点击编辑设置,我们将看到操作系统定制对话框打开在常规标签页上,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_6.jpg

图 15.6 – 定制我们的 Raspberry Pi 操作系统

在前面的截图中,我已设置了我的主机名、希望在 Pi 上使用的用户名和密码,最重要的是,我配置了无线局域网设置,使其能够自动连接到我想要使用的 Wi-Fi 网络 ShedWifi,这是我小屋里的 Wi-Fi 网络。现在,我们需要切换到SERVICES选项卡以启用 SSH。默认情况下,当我们点击启用 SSH时,它会选择使用密码认证。我将继续使用此设置,但我们也可以配置为仅允许公钥认证。如果我们点击保存,会收到警告,提醒我们目标 SDHC 卡上的所有数据将被覆盖,并要求我们确认此操作。一旦确认,几分钟后,我们会收到一个写入成功的弹窗,告诉我们可以移除 SDHC 卡。让我们照做——将其插入 Pi 并开机。SERVICES选项卡在以下截图中显示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_7.jpg

图 15.7 – Raspberry Pi Imager 的 SERVICES 选项卡

一旦 Pi 启动,我们应该能够在网络上看到它,前提是我们的客户端与 Pi 在同一个子网内。为了测试这一点,在客户端的 PowerShell 会话中,输入以下内容:

Test-NetConnection <pi hostname> -InformationLevel Detailed

我们应该能看到 IPv6 和 IPv4 地址,以及许多其他信息:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_8.jpg

图 15.8 – 确认我的 Pi 是否存在

如我们所见,我的 Pi 已经获得了它的 IP 地址,正如预期的那样,现在我可以连接到它。

让我们试试吧。

使用 PowerShell 连接到 Pi

在客户端的 PowerShell 会话中,输入以下内容:

ssh <pi hostname>

如果客户端和主机上的用户名不同,你可以输入以下内容:

Ssh <username>@<hostname>

以下截图显示了该过程的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_9.jpg

图 15.9 – 使用 SSH 连接到 Pi

在第 1 行,我们使用 ssh poshpi 命令开始了一个 SSH 会话连接到 Pi。

在第 2 行,我们被提示同意连接,因为无法验证 Pi 的真实性。我们大概可以在这里输入 yes;请注意,单独输入 Y 是无效的。

在第 3 行,我们被要求输入用户的密码——在我的情况下,这是 nickp

在第 4 行,我们看到来自 Pi 的 bash 提示符——即 nickp@poshpi.local:~ $

现在,我们需要安装 PowerShell。我们可以像之前那样安装——也就是通过将微软脚本的内容复制到命令行,或者在 nano 中创建一个 bash 脚本。安装完成后,我们可以使用以下命令启动 PowerShell:

~/powershell/pwsh

另外,我们可以创建一个符号链接symlink),像这样:

sudo ln -s ~/powershell/pwsh /usr/bin/pwsh

然后,未来我们只需输入 pwsh,就像这样:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_10.jpg

图 15.10 – 设置符号链接以运行 PowerShell

在第 1 行,我们创建了一个符号链接,在第 2 行,我们启动了 PowerShell。

在第 3 行,我们正在通过PS /home/nickp>提示符在 Linux 上运行 PowerShell;我们可以调用$PSVersionTable变量查看我们运行的 PowerShell 版本。

最后,在第 4 行,我们使用Ctrl + Break退出 SSH 会话并返回到在 Windows 客户端上运行的 PowerShell 会话——PS C:\users\nickp>——在第 5 行。我们还可以使用Ctrl + D注销会话。

很棒!让我们来看另一种连接到无头 Pi 的方法:使用 VS Code。

使用 VS Code 连接到 Pi

这种方法适用于我们希望通过 SSH 连接的任何计算机,包括 Linux。我们将使用一个名为Remote-SSH的 VS Code 扩展,在搜索栏中输入,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_11.jpg

图 15.11 – 安装 Remote – SSH 扩展

找到扩展后,点击它,然后在中间窗格点击安装,如前面的截图所示。

完成此操作后,我们将在左侧边栏中看到一个桌面图标。点击该图标可以打开远程资源管理器窗口并设置与 Pi 的 SSH 连接。我们将被要求选择远程类型——远程计算机或 WSL 目标。我们选择远程计算机,所以选择它,然后点击SSH

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_12.jpg

图 15.12 – 选择 SSH

点击ssh <username>@<hostname>。在我的情况下,我输入了以下内容并按下Enter

ssh nickp@poshpi

接下来,我们将被要求选择一个 SSH 配置文件来更新。我正在更新我的个人文件,C:\Users\nickp\.ssh\config。然后,我们将看到一个消息框提示我们主机已添加!远程资源管理器区域,点击远程(隧道/SSH)旁边的刷新图标,如图 15.13中绿色高亮显示的部分;我们应该会看到新主机出现在SSH子部分,旁边有两个图标——一个用箭头表示的是打开当前窗口中的主机,另一个用红色高亮显示的是在新窗口中打开。点击新窗口中连接图标:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_13.jpg

图 15.13 – 打开与 Pi 的连接

接下来,我们将被要求选择远程主机的平台;选择 Linux。我们还需要输入密码。完成后,我们需要等待一两分钟来完成设置,并关闭一两个消息窗口。最后,我们将会看到一个新窗口连接到我们的 Pi;我们知道这一点是因为在左下角会显示一个框,里面写着SSH: 。在这个窗口中做的所有操作都发生在 Pi 上。很酷吧?这意味着,我们不再需要在客户端上编写脚本并将其传输到 Pi,而是可以直接从 VS Code 编写到 Pi。

好了,准备好了。我们可以像本书中展示的那样,在 Pi 上使用 PowerShell,但那并不是我使用 Raspberry Pi 的主要目的。接下来,我们将看看如何在 Pi 上使用 PowerShell。

在 Raspberry Pi OS 上运行 PowerShell

Raspberry Pi 的魅力在于它能以多种方式与外部世界连接,从摇杆控制器到相机,再到传感器、马达,甚至……哦,所有东西。在这一章中,我们将学习如何使用 GPIO 引脚让 LED 闪烁,但首先,我们需要了解如何与 GPIO 交互。这里有两个选项,都不太完善。更好的选择是安装一个新的操作系统:Windows 10 IoT Core。这本身就需要一到两章的篇幅,而且并未真正解决在 Raspberry Pi OS 上运行 PowerShell 的问题。另一种方法是使用 PowerShell IoT 模块。自 2020 年以来,这个模块没有更新,似乎也不支持较新的 Pi 4B 版本,但它在旧版本上运行相当不错,我们只能希望它未来能得到更新。我打算利用我抽屉里的 Pi 3B。

安装 IoT 模块

我们在这里使用硬件,因此需要以 root 权限启动 PowerShell,输入以下命令:

sudo pwsh

一旦进入 PowerShell,我们可以像往常一样安装模块:

Install-Module Microsoft.powershell.iot

最后,我们可能需要从 GitHub 克隆仓库,这样我们就可以访问示例了。我们可以通过以下命令实现:

git clone https://github.com/PowerShell/PowerShell-IoT.git

这将把 GitHub 仓库中的所有代码安装到我们选择文件夹下的新文件夹中:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_14.jpg

图 15.14 – 安装 PowerShell IoT 模块并克隆 GitHub 仓库

这让我们可以访问 Examples 文件夹中的所有示例模块。里面包括一些有趣的工具,我们可以用来配合各种传感器,比如 BME280 环境传感器。

下一步是导入模块并检查它是否正常工作:

Import-Module Microsoft.PowerShell.IoT
Get-GpioPin 15

如果运气好的话,我们会看到类似以下的内容:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_15.jpg

图 15.15 – 导入模块并检查给定 GPIO 引脚的电压

我们是否要探索一下这个模块中的 cmdlet?

探索 IoT 模块

在 IoT 模块中,有六个 cmdlet 用于操作 Pi 上的三种 I/O 接口:简单 GPIO、I2C 和 同步外设接口 (SPI)。令人困惑的是,这三个接口都使用 GPIO 引脚。这有时会使我们很难选择哪些引脚用于特定的目的:

  • 简单的 GPIO 读取或设置特定 GPIO 引脚的电压,使用一对名为 Get-GpioPinSet-GpioPin 的 cmdlet。我们很快就会使用到这个。

  • I2C 使用 Get-I2CRegisterSet-I2CRegister,还有一个 Get-I2CDevice cmdlet。

  • 最后是 SPI。这相当复杂,我们在本书中不打算深入探讨。这里只有一个 cmdlet:Send-SPIData

让我们更仔细地看看我们最常使用的五个 cmdlet。这些 cmdlet 的帮助文件可以在 /home/<username>/PowerShell-IoT/docs/help/ 文件夹中找到,但我们在这里简单介绍它们的基本用法:

  • Get-GpioPin:此 cmdlet 获取指定 GPIO 引脚的电压。它有三个参数:

    • -Id,它接受一个 Int32 值,指定我们要查看的 GPIO 引脚。

    • -PullMode,可以设置为 OffPullDownPullUp,某些芯片组可能需要此设置,但树莓派不需要。默认值为 Null

    • 一个 -Raw 开关,它返回 HighLow 的值。

  • Set-GpioPin:此 cmdlet 将指定引脚的电压设置为 HighLow。它有三个参数:

    • -Id,它接受一个 Int32 值,指定引脚。

    • -Value,它接受 HighLow 值。

    • -PassThru,默认情况下,该 cmdlet 不返回任何内容。如果我们希望它返回一个 PowerShell 对象,确认已设置该值,那么可以使用此参数。

  • Get-I2CDevice:此 cmdlet 创建一个 I2C 设备对象,并为其分配一个友好的名称,之后可以与 *-I2Cregister cmdlet 一起使用。它有两个参数:

    • -Id,它接受一个 Int32 值,并指定设备的地址

    • -FriendlyName,用于为设备分配一个字符串

  • Get-I2Cregister:此 cmdlet 获取特定设备上寄存器中的值。它有四个参数:

    • -Device,它接受一个 I2C 设备对象

    • -Register,它接受一个 Uint16 值,指定我们要读取的设备上的寄存器

    • -Raw,它返回寄存器中存储的值,而不是 I2CdeviceRegisterData 对象

    • -Bytecount,它接受一个字节值,并指定数据中预期的字节数

  • Set-I2Cregister:此 cmdlet 设置设备上的寄存器值。它有四个参数:

    • -Device,它接受一个 I2C 设备对象。

    • -Register,它接受一个 Uint16 值,指定我们要设置的设备上的寄存器。

    • -Data,一个字节值,用于写入寄存器。

    • -PassThru,默认情况下,该 cmdlet 不返回任何内容。如果我们希望它返回一个 PowerShell 对象,确认已设置该值,那么可以使用此参数。

就是这样。最好的方法是亲自上手并尝试,看看它们如何工作。

简单的物理计算

Raspberry Pi 的物理计算使用的是板子右侧的 GPIO 引脚,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_16.jpg

图 15.16 – Raspberry Pi 的 GPIO 引脚

需要记住的重要事项是,接地引脚为负,电压引脚 3V3 和 5V 为正且始终开启。虽然 GPIO 引脚可能有其他特殊用途,但它们是我们可以开关的引脚——它们会输出正电流。

当我们学习一种新的编程语言时,通常会从最简单的程序开始——Hello World

Write-Output "Hello World"

在 Python 中,我们可以编写如下代码:

print("Hello World")

物理计算稍微不同——我们编写一个程序让 LED 闪烁。平台似乎无关紧要,这就是我们开始的地方。例如,让 Arduino 闪烁 LED 的程序是用 C 语言写的,类似这样:

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

v``oid setup()初始化板载 LED 为输出,并在每次重置 Arduino 时运行一次。void loop()命令设置了一个在 Arduino 打开时持续运行的循环(void只是告诉 Arduino 不产生输出)。digitalwrite()命令将电压设置为HIGH(开)或LOW(关)。在树莓派上,这在 Python 中看起来也很相似:

From RPi.gpio import LED
Red_led = LED(17)
Red_led.blink(on_time=1, off_time=1)

让我们在 PowerShell 中试试。首先,我们需要根据以下示意图设置硬件:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_17.jpg

图 15.17 – 如何设置组件

LED 上会有两条腿,其中一条比另一条长。长腿需要连接到电路的正极,短腿需要连接到负极或接地端。树莓派输出的电流对 LED 来说太高,所以我们需要使用大约 300-400 欧姆的电阻来稍微降低电流。如果电阻大于 1K 欧姆,可能会导致 LED 不亮;如果小于 200 欧姆,电流过大可能会烧坏 LED(如果 LED 比较便宜的话)。面包板插孔是按五列连接的,因此电阻会跨越两列。

现在,我们需要编写一些 PowerShell 代码来控制 LED 的开关。让我们使用 VS Code 的 SSH 远程连接到树莓派,打开一个新文档,命名为blink.ps1

让我们首先导入 IoT 模块:

Import-Module Microsoft.PowerShell.IoT

现在,我们需要一个始终运行的循环:

While ($true) {
}

在这个循环中,我们需要使用Set-GpioPin来控制 LED 的开关。如果你遵循了前面的示意图,你应该使用 17 号引脚。

我们还需要使用Start-Sleep来在每个命令之间等待;否则我们可能会把 LED 烧坏:

Import-Module Microsoft.PowerShell.IoT
while ($true) {
    Set-GpioPin -Id 17 -Value "High"
    Write-Host "LED on"
    Start-Sleep 1
    Set-GpioPin -Id 17 -Value "Low"
    Write-Host "LED off"
    Start-Sleep 1
}

我在这里添加了几行输出代码,以便我能够在不使用 YouTube 链接的情况下合理展示。运行结果在 VS Code 中是这样的:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_15_18.jpg

图 15.18 – 闪烁的 LED

正如我们所见,LED 的状态每秒变化一次;直到手动停止脚本,它将继续变化。我们可以看到它与我们为 Arduino 编写的 C 程序和 Python 程序进行比较的方式。

Examples文件夹中还有一个 LED 模块示例——非常值得看看,看看他们是如何做的。

这一章差不多就到这里了。让我们总结一下我们学到的内容。

总结

我们从快速了解树莓派开始,包括它的功能、设计用途以及人们如何使用它。我们看了不同的系列;主要的 B 系列单板计算机、Zero 迷你版以及 Pico 单芯片板。我们了解到,由于 Zero 和 Pico 系列的芯片架构不匹配,我们只能在 B 系列上安装 PowerShell。

接下来,我们查看了使用 Microsoft 提供的脚本在 Pi 上安装 PowerShell 的不同方法。然后,我们发现安装 VS Code 在 Pi 上是如此简单,因为它已包含在官方的 Raspberry Pi 仓库中。

虽然有一些适合作为桌面 PC 替代品的 Raspberry Pi 型号,但大多数人将使用没有屏幕或鼠标的 Raspberry Pi —— 即在无头模式下。我们了解了如何在无头模式下设置 Raspberry Pi,然后如何通过 PowerShell 使用 SSH 连接到它,接着再看了如何通过 VS Code 直接在 Pi 上方便地工作。

然后,我们讨论了 Pi 最受欢迎的应用案例之一:物理计算。这是我们与物理世界中的传感器和物体进行交互的地方。我们查看了 Microsoft 模块,用于与 Raspberry Pi 上的 GPIO 进行交互,并通过一个脚本来实现 LED 闪烁的功能。

这就是我们将要查看的所有环境内容。在下一章也是最后一章中,我们将探讨如何访问 PowerShell 所依赖的 .NET 系统,并讨论接下来的步骤。

问题

  1. 为什么我们不能在 Raspberry Pi Zero 或 Pico 上安装 PowerShell?

  2. VS Code 在 Windows 机器上的 SSH 配置存储在哪里?

  3. 用于测试 Raspberry Pi 与另一台设备的网络连接的 PowerShell cmdlet 是什么?

  4. 如何从 PowerShell 创建到无头 Pi 的 SSH 连接?

  5. 我们已经将安装脚本保存为 Install.sh。我们该如何运行它?

  6. 为什么我们可能要创建指向 pwsh 可执行文件的符号链接?

  7. 我们如何创建指向 pwsh 的符号链接?

  8. 如何从 GPIO 引脚获取 HighLow 的值?

  9. Raspberry Pi OS 是什么样的操作系统?

进一步阅读

第十六章:使用 PowerShell 和 .NET

现在,我们来到了最后一章。我们将探讨 PowerShell 7 所依赖的产品——.NET,以及如何利用它扩展我们在 PowerShell 中能做的事情。需要注意的是,这一章只能提供一个简短的概览;.NET 是一个庞大的主题,关于它的书籍远远多于关于 PowerShell 的书籍。PowerShell 只是一个基于 .NET 编写的应用程序,它仅触及了我们在 .NET 中可以做的一部分事情。话虽如此,还是让我们来看看它的工作原理,以及我们可以用它做一些什么令人兴奋的事情。

本章将涵盖的主要内容如下:

  • 探索 .NET

  • .NET 的组件

  • 在 PowerShell 中使用 .NET

  • 使用 .NET

探索 .NET

.NETdot-net)是一个软件框架。它是免费的、开源的,可以用来编写 Web 应用程序、命令行应用程序和运行在 图形用户界面 (GUI) 下的应用程序。它基于专有的 .NET 框架,这个框架是 Windows 操作系统所使用的。它可以与多种编程语言一起使用,包括 C#、F# 和 Visual Basic .NET。让我们来详细了解一下吧?

软件框架解释

软件框架是构建应用程序的工具。一些框架是为特定目的和特定语言编写的;AngularJS 是一个用于开发 JavaScript Web 应用程序前端的框架。PhaserJS 是一个游戏开发框架,同样适用于 JavaScript。虽然框架包含了许多类库,但与类库不同,框架需要在其结构内工作。类库为我们的代码提供工具,可以随意使用;而框架则规定了应用程序的基本结构,我们需要在此基础上提供具体的实现。

.NET 实现了 公共语言基础设施 (CLI),允许不同的高级语言(如 C#)在多个操作系统平台上使用,而无需为每个架构重写代码;这就是 PowerShell 7 如何能够在 Intel 和 ARM 处理器上运行的原因。通过安装正确版本的 .NET,我们的 PowerShell 代码就能在任何地方运行。

一个名为 CoreFX 的 .NET 组件包含了 .NET 类库、接口和值类型,组成了 框架类库。不过,.NET 提供的不仅仅是类库。 .NET 应用程序在一个名为 CoreCLR 的虚拟机中运行,就像 Java 应用程序在 Java 虚拟机中运行一样。

公共语言基础设施

CLI 是由微软开发的开放技术标准,主要只存在于 .NET 的不同变种中,尽管也有一个叫做 Mono 的开放开发平台也使用它。CLI 定义了五个要素:

  • 公共类型系统(CTS) – 可由编程语言通过框架访问的类型集合。

  • 元数据,用于描述程序结构。

  • 公共语言规范(CLS) – 使用框架的规则。

  • 虚拟执行系统(VES) – 它加载并执行应用程序。它使用元数据在运行时执行用兼容语言生成的代码,并将其编译成平台无关的 公共中间语言(CIL),然后将其即时编译为平台特定的机器语言。在 .NET 中,VES 是通过 CoreCLR 组件实现的。

  • 标准库,提供常用功能,如访问网络和文件。

让我们来看一下 CoreCLR 组件。

公共语言运行时 – CoreCLR

CoreCLR 提供了一个公共语言运行时,这是一个位于应用程序和操作系统之间的层。它的原理类似于 PowerShell 程序;.NET 应用程序需要在机器上运行 .NET 才能将应用程序代码解释为机器代码。这意味着 PowerShell 需要 .NET 运行,因为 PowerShell 本身是一个 .NET 应用程序。

然而,CoreCLR 不仅仅提供运行时。它还包括额外的服务,如内存管理(为应用程序分配虚拟内存的一部分)、垃圾回收(回收不再需要的未使用内存)和线程管理。这意味着,当我们使用 .NET 编写应用程序时,我们无需担心内存泄漏或内存寻址错误,因为这些都由 CoreCLR 为我们处理。

幸运的是,我们不需要知道 CoreCLR 是如何工作的,就能在 PowerShell 中使用 .NET。我们感兴趣的大部分内容都在库中 – CoreFX。

框架类库 – CoreFX

CoreFX 包含了 .NET 使用的类库,其中包括类型、函数和类。例如,所有 PowerShell 数据类型都是可用 .NET 类型的一个子集。我们已经看到这一点;在 第四章《PowerShell 变量与数据结构》中,我们看到改变 PowerShell 数组内容是资源密集型的,因为每次改变数组时,我们会创建一个新数组并删除旧数组。我们看到,解决这个问题的方法之一是使用一个在 PowerShell 中原生不可用的 .NET 类型 ArrayList,就像这样:

$ArrayList = [System.Collections.ArrayList]@()
1..10000 | ForEach-Object { $Null = $ArrayList.Add($_) }

我们使用 [System.Collections.ArrayList] 完整的 .NET 类型来将数组设置为 ArrayList。有时候,使用 .NET 确实是这么简单。

.NET 历史

.NET Framework 首次发布于 2002 年,旨在创建 Windows 应用程序。它引入了微软的托管代码概念,即只与 CLI 交互的代码。托管代码可以更严格地控制资源使用和安全性,并且由于只接触 CLR 而不是底层操作系统,它也不太容易导致系统崩溃(蓝屏),这就是其特点之一。随着时间推移,微软的许多最受欢迎的应用程序,如 Microsoft Exchange Server,都是用托管代码编写的,需要.NET Framework 来运行。.NET Framework 仅能在 Windows 上运行,并包含许多 Windows 特定的功能。

在 2014 年,微软发布了.NET Core,这是一个开源的、跨平台实现 CLI 的项目。它与.NET Framework 共享许多特性,但并非全部,并且它还包含了许多.NET Framework 中没有实现的功能,特别是在不同操作系统上运行的能力。2022 年,微软发布了一个新版本的.NET Core,简称.NET 5;其意图是最终取代.NET Framework。就像 PowerShell 和 Windows PowerShell 一样,实际上,两个版本现在并存。截至撰写本文时,最新的.NET Framework 版本是 2022 年 8 月发布的 4.8.1 版本,而.NET 每年发布一次,大约在 11 月左右;最新版本是 2023 年 11 月发布的.NET 8.0 版本。

那么,我们可以用.NET 做什么呢?让我们来看看。

.NET 的用途

.NET Framework 是为创建 Windows 应用程序而开发的——尽管我们可以将其用于命令行程序,它包括Windows Presentation FoundationWPF)框架、用于 Internet 应用程序的 ASP.NET 以及用于图形应用程序的 Windows Forms。

当前的.NET 版本包括云原生应用程序和 Azure 上的无服务器函数的库、跨平台桌面应用程序和游戏、使用.NET Multi Application User InterfaceMAUI)的移动应用程序、带有 ML.NET 的机器学习应用程序以及带有.NET IoT 的物联网应用程序。

然而,最成熟的库是为 Windows 开发的,它们包括用于 WPF、Windows Forms 和Universal Windows PlatformUWP)的 Windows 桌面应用程序库以及允许我们将应用程序作为服务运行的 Windows 服务库。

谈到.NET,可以轻易写一整套的书籍,而且许多人已经这么做了。然而,我们更感兴趣的是如何在 PowerShell 中使用.NET。我们将重点关注.NET 库以及如何从 PowerShell 访问其内容,进而访问我们希望使用的 API。让我们首先看一下.NET 库的结构。

.NET 的组件

在我们开始使用 .NET 库之前,我们需要理解它们的结构。成员(属性、方法等)包含在类型中,而类型又包含在命名空间中。这就是逻辑类型封装。还有物理类型封装。这些逻辑结构在物理上由程序集保存。我们在 PowerShell 中已经看到过这些组件。我们先从程序集开始。

程序集

程序集是类型和支持它们所需资源的集合。它们可以是静态的,从文件加载,或者动态的,仅存在于内存中。PowerShell 在启动时会加载一组默认程序集,然后随着我们导入模块,这个列表会扩展。我们可以通过以下方式查看加载的程序集列表:

[System.AppDomain]::CurrentDomain.GetAssemblies()

这将输出如下表格:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_1.jpg

图 16.1 – 枚举程序集

我们可以查看这些静态程序集的版本和文件存储位置。还有一列,GAC,表示 False。我们可以通过使用 .NET 包管理器 NuGet 安装通常存储在 GAC 中的程序集来使用它们;只不过我们不能从 GAC 访问它们。我们还可以看到这些程序集是 .dll 文件,这在我们讨论二进制模块时提到过,第十一章创建我们的第一个模块,我们还看到二进制模块是 .NET 程序集的一种类型。我们可以将加载 .NET 程序集看作类似于加载二进制模块。PowerShell 在启动时加载一组默认程序集,这些程序集定义了我们可以使用的类型。要访问其他类型,我们需要加载更多程序集。

类型

我们第一次遇到类型是在 第四章PowerShell 变量和数据结构 中。PowerShell 类型是 .NET 中类型的一个子集。在本章之前,我们在创建 ArrayList 对象时使用了一个 .NET 类型。类型用方括号括起来。

类型与类

在类型理论中,类型是一个抽象概念,而类是一组创建给定类型对象的指令。一个对象属于某种类型——例如,一个字符串或一辆虚构的自行车。类是类型的实现。对象是类的实例。例如,我们可以有多个不同的虚拟自行车类,它们都属于类型 Imaginary.Bike,但它们可能具有不同的特征——例如,猿把手或下拉把手。然后,我们可以为每个类创建多个实例(即对象)——在这种情况下,虚拟自行车。

在实践中,PowerShell 中的类是用户定义的,而类型是由 .NET 提供的——当然,除非情况并非如此。

枚举类型

枚举是常量值的列表。虽然我们之前没有讨论过它们,但我们肯定用过它们。在第十章,*错误处理 – 哎呀!出错了!*中,我们看到自动变量$ErrorActionPreference以及通过更改该变量的值,如何临时控制 PowerShell 处理错误的方式。我们可以通过调用变量的GetType()方法来检查$ErrorActionPreference变量的类型,并且可以在以下截图中看到它是System.Enum类型:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_2.jpg

图 16.2 – 如何设置你?让我数一数有哪些方式

我们可以使用GetEnumValues()方法列出可能的值,并看到熟悉的可能值列表。$ErrorActionPreference只能拥有这些值,且无法更改。

类是对象的逻辑定义,它定义了对象的属性和方法——就像一份食谱。它是某种类型的实现。回到第四章PowerShell 变量与数据结构,我们通过创建类型为Imaginary.Bike的三个对象,定义了这些对象及其属性,并将其标记为Imaginary.Bike。我们也可以定义一个名为Imaginary.Bike的类,并赋予它相同的属性和方法,使用构造函数来创建该类的实际实例。当我们希望能够轻松且可重复地创建对象时,我们使用类。

命名空间

命名空间类似于文件系统中的文件夹;我们在第十三章使用 PowerShell 7 和 Windows中讨论 CIM 类时看到了命名空间的使用。大多数 PowerShell 类型和函数都位于System.Management.Automation命名空间中。当我们与文件系统交互时,我们使用System.IO命名空间。我们在 PowerShell 中引用命名空间时,不需要写System,因为会自动搜索System命名空间;Management.Automation在功能上与System.Management.Automation相同。不幸的是,我们需要为任何不在System命名空间中的类型指定命名空间,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_3.jpg

图 16.3 – 访问命名空间

Path类型定义了对象的文件路径,并位于System.IO命名空间中。如果我们在第 1 行没有指定命名空间来引用它,就会出现错误。在第 2 行,我们指定了完整的命名空间,但在第 3 行,我们可以看到无需指定System部分的命名空间,因为它是隐式的。

我们还可以使用using关键字来加载命名空间,如下所示:

using namespace System.IO

运行此操作后,我们可以像这样调用Path类型,而无需指定命名空间:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_4.jpg

图 16.4 – using关键字的作用

我们在第十一章《创建我们的第一个模块》中看到过using关键字,我们了解它是脚本中加载模块的推荐方式。正如我们所看到的,我们也可以使用它来加载命名空间和程序集。不幸的是,在控制台中,只有最新的using语句会生效,因此如果我们在当前会话中使用它访问另一个命名空间,就会失去对System.IO命名空间的访问权限。脚本允许多个using语句,通常写在脚本的开头;它们只能被注释符号所前置。我们可以通过使用分号(;)分隔using语句,在控制台中加载多个命名空间。

成员

类型和类有成员;在本书中,我们使用了Get-Member命令来检查对象的属性和方法,并且我们已经看到对象是某种特定类型的实例。 .NET 类型有一个我们之前没有见过的成员——构造函数。构造函数提供了实例化给定类对象的方式,并与类同名。当我们对类运行Get-Member时,构造函数不会出现,也无法直接调用。构造函数可能有参数,我们可以使用这些参数来填充新对象的数据成员,也可能没有;如果没有参数,则对象会被创建时具有一组空值属性。构造函数可能有重载,可以在构造对象时传递不同的参数集。大多数 .NET 类都有一个自动构造函数new(),这是 PowerShell 添加的一个静态方法。

让我们看一个例子。[string]类型是不可变的;当我们修改字符串时,我们会销毁旧字符串并创建一个新字符串。如果我们需要字符串频繁变化,这可能会导致性能问题,这与我们在数组中看到的问题类似。在.NET 中有一个定义可变字符串的类,它解决了这个问题;它被称为StringBuilder类,该类的对象是System.Text.StringBuilder类型。如果我们使用默认的自动构造函数创建一个新的StringBuilder对象,我们会得到一个具有三个属性的对象:CapacityMaxCapacityLength,这些属性的单位是字符。如果我们不带括号调用new()方法,我们就能看到所有可能的重载列表:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_5.jpg

图 16.5 – StringBuilder类的重载

正如我们所看到的,我们可以向new()方法传递不同的参数组合,从而构造具有不同属性的StringBuilder对象。让我们试试看。输入以下内容:

$string1 = ([System.Text.StringBuilder]::new(32))
$string2 = ([System.Text.StringBuilder]::new('32'))

现在,如果我们调用$string1$string2,我们会看到$string1是空的,且其容量为 32;$string2的长度为 2,容量为 16。这是因为我们对$string1调用了重载new(int capacity),对$string2调用了重载new(string value)

我们可以使用ToString()方法查看内容,如下所示:

$string1.tostring()
$string2.tostring()

$string1 是空的,而 $string2 包含 '32' 字符串。如果我们使用 GetType() 方法,我们可以看到两者都是 StringBuilder 对象。如果我们将它们传递给 Get-Member cmdlet,我们还可以看到 new() 方法没有列出,因为它是一个构造函数。

版本管理

程序集是一个包含称为程序集清单的元数据的 .dll 文件,清单列出了文件的内容以及文件的名称和版本。强名称的概念是在 .NET 中引入的;一个强名称由模块的名称、版本以及用于验证文件作者的加密哈希组成。当 .NET 程序链接到一个强名称程序集时,文件的名称、版本和哈希必须与链接的强名称匹配。如果我们只是简单地用一个较新的版本替换 .dll 文件,那么程序将无法加载它。这导致了具有相同版本号的 .dll 文件的不同版本,以防止引入破坏性更改。太棒了。

还有许多其他组件,但这些是我们在使用 .NET 和 PowerShell 时最需要注意的内容。现在,让我们来看看 PowerShell 如何利用 .NET。

在 PowerShell 中使用 .NET

在本节中,我们将查看 PowerShell 如何访问 .NET 库的详细信息。我们将了解默认的程序集、PowerShell 如何查找类型,以及另一种创建对象的方法。

为什么要在乎?

PowerShell 和 C# 都是 .NET 家族的一部分,因此它们可以很好地协同工作,因为它们基于相同的 .NET 基础。它们共享许多功能,例如类和库。我们可以通过使用 Add-Type 在 PowerShell 中调用 C#,这让我们在运行 PowerShell 脚本时编译并运行 C# 代码。这样我们就能利用 PowerShell 的简洁和易用性,但当需要时,C# 也能随时调用,而不必编写整个程序。

PowerShell 程序集

我们在章节开始时看到,可以通过以下语句列出已加载的程序集:

[System.AppDomain]::CurrentDomain.GetAssemblies()

AppDomain 是一个封装和隔离执行环境的类;它有点像 PSSession,但更加安全;每个 PSSession 实例共享一组程序集,而每个 AppDomain 实例加载自己的程序集。CurrentDomain 获取当前执行环境。双冒号(::)是 C# 命名空间别名运算符;我们需要使用它来访问别名命名空间的成员,它位于两个标识符之间。让我们像这样再次运行语句:

[System.AppDomain]::CurrentDomain.GetAssemblies() | select FullName

然后,我们可以看到已加载程序集的强名称列表:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_6.jpg

图 16.6 – 默认程序集及其强名称

请注意,每个强名称包含一个短名称、一个版本、一个文化标识符和一个加密密钥,该密钥标识作者。

动态程序集加载

自动加载适用于像pwsh.exe这样的已编译程序,但它依赖于可执行文件中的所需程序集列表。我们也可以将所需的程序集添加到模块清单中的RequiredAssemblies元素。如果在编写脚本时需要加载非默认程序集,我们可以使用前面的using关键字、Add-Type cmdlet,甚至使用Import-Module cmdlet,如果程序集在一个.dll文件中。

Add-Type cmdlet 有五个参数集;其中三个用于定义新类型,但我们也可以使用它从指定路径导入程序集,或仅从程序集导入我们需要的类型,例如以下内容:

Add-Type -AssemblyName PresentationCore,PresentationFramework

这将添加所需的程序集,以便从 PowerShell 中调用简单的 Windows GUI 元素。

一旦我们导入(或创建)了新的类型,就可以使用New-Object cmdlet 来创建该类型的实例。

创建类型的实例

New-Object cmdlet 创建一个类型的实例。该类型必须存在于 PowerShell 默认的程序集内,或者我们必须先使用Add-Type导入它。New-Object很容易使用。我们只需要提供TypeName和一个与可用重载匹配的参数列表。例如,StringBuilder类型的一个重载允许使用一个字符串来定义新对象的值,另一个整数来定义初始容量。请注意,它接受一个字符串值(System.Text.String.Builder),而不是命名空间和命名空间别名限定符([System.Text.StringBuilder]::),所以我们可以这样做:

$loveit = (New-Object -TypeName System.Text.StringBuilder -ArgumentList "i love powershell", 128)

这将创建一个名为$loveit的变量,它包含"i love PowerShell"字符串,并且初始容量为 128 个字符:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_7.jpg

图 16.7 – 喜欢它

我们可以在前面的截图中看到,初始容量是 128,而如果我们只传递一个字符串值的话,预计它应该是 17,即字符串的长度。请注意,我们需要理解我们希望使用的构造函数的重载所需的参数——在本例中是"i love powershell"128。例如,如果我们提供两个字符串,则会出错。

另一种选择是使用-Property参数,它接受一个包含属性名称和所需值的哈希表。我们将在下一节中看到如何使用它,但请注意,如果你拼写错误,PowerShell 会将拼写错误的属性添加到对象中,而不是告诉你有错误。

让我们尝试几个例子,以便更好地了解如何在 PowerShell 中使用 .NET。

使用 .NET

在本节中,我们将尝试两个示例——一种触发操作的替代方法,比如脚本,以及如何从 PowerShell 中调用 Windows GUI 元素。

任务调度程序的替代方案

在这个示例中,我们将创建一个定时器对象,然后使用Register-Event cmdlet 在定时的间隔内触发一个操作。

首先,让我们创建一个定时器:

$timer = (New-Object -TypeName System.Timers.Timer -Property @{
AutoReset = $true
Interval = 5000
Enabled = $true
}
)

现在,我们需要注册事件并定义一个操作:

Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier Test -Action {Write-Host "hello"}

现在,让我们开始计时,操作如下:

$timer.start()

然后,我们应该看到hello字符串每隔五秒钟出现在屏幕上,直到我们输入以下内容:

$timer.stop()

这就是它在我机器上的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_8.jpg

图 16.8 – hello

我们可以看到每当计时器达到 5000 毫秒时,命令就会被触发。酷吧?

让我们尝试创建一个 GUI 消息框。

创建 GUI 对象

这个例子将在 Windows 中创建一个弹出消息框,包含一对“是/否”按钮。按下的按钮值将记录在 PowerShell 会话中:

Add-Type -AssemblyName PresentationCore,PresentationFramework
$Button = [System.Windows.MessageBoxButton]::YesNo
$Title = "PowerShell for Everyone"
$Body = "Do you love PowerShell?"
$Icon = [System.Windows.MessageBoxImage]::Warning
[System.Windows.MessageBox]::Show($Body,$Title,$Button,$Icon)

我们可以将这个与前面的计时器对象结合使用,显示一个消息框,允许用户取消长时间运行的脚本。这是它在我机器上的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_16_9.jpg

图 16.9 – 当然你会

请注意,默认答案是。我们还可以使用响应来设置参数、添加事件或触发条件语句。

让我们总结一下本章的内容。

总结

我们首先了解了什么是.NET——一个软件框架。我们了解到它是基于公共语言基础结构(Common Language Infrastructure)并且类似于 Windows 中的.NET Framework,但并不完全相同。我们看到它包含了自己的运行时 CoreCLR 和一套库 CoreFX。我们了解了.NET 和.NET Framework 之间的关系以及它们如何共存。我们看到.NET 可以用于许多领域,包括机器学习和物联网应用,但它主要用于 Azure 和 Windows。

我们了解了.NET 的各个组件,并理解了它们如何相互关联,并在 PowerShell 中表示。我们看到了构造函数成员,这是用于实例化对象的一种特殊类型的方法,并理解了为什么版本控制经常让人困惑。

然后,我们查看了如何从 PowerShell 访问.NET 库的具体方法,学习了动态加载,并看到了如何使用New-Object cmdlet 来创建类的实例。

最后,我们通过两个例子演示了可以使用.NET 做的事情——创建事件计时器和 Windows 消息框。

接下来呢?这取决于你的需求。如果你想了解更多 PowerShell 的内容,有很多很棒的书籍,比如 Packt 出版的 Chris Dent 的《Mastering PowerShell Scripting》,或者 Bruce Payette 等人编写的绝对经典之作《Windows PowerShell in Action》。无论你选择哪本书,你需要做的就是多加练习。任何语言,最好的学习方式就是用它。若你对.NET 感兴趣,PowerShell 是一个不错的起点,但迟早你会想使用编译型语言,比如 C#。虽然在 PowerShell 中编写机器学习应用程序从技术上来说是可行的,但我认为用 C#做会容易得多。

就这样。这本书到此为止。感谢你与我一起坚持读完,希望你读得和我写得一样愉快。我可以向你保证,我在这个过程中学到了很多,希望你也有所收获。希望你像我享受陪伴一样也享受我的陪伴。

练习

  1. 如何在 PowerShell 中创建 .NET 类的新实例?

  2. PowerShell 中用于将 .NET 程序集添加到会话的命令是什么?

  3. 如何在 PowerShell 中调用 .NET 类的静态方法?

  4. 在 PowerShell 中,如何访问 .NET 类的静态属性?

  5. 如何在 PowerShell 中调用带参数的 .NET 构造函数?

  6. 用于从文件加载 .NET 程序集的 PowerShell cmdlet 是什么?

  7. 如何在 PowerShell 中确定对象的 .NET 类型?

  8. 在 PowerShell 中,如何列出 .NET 对象的所有方法?

  9. 在 PowerShell 中,调用 .NET 对象实例方法的语法是什么?

  10. 如何在 PowerShell 中访问 .NET 对象的实例属性?

进一步阅读

活动和练习的答案

第一章

活动

  1. 你可以像这样使用 ADD_FILE_CONTEXT_MENU_RUNPOWERSHELL

    kill(). Charming, right? CloseMainWindow() might work for a graphical process, and Close() will ask politely, but kill() should do it. Note that sometimes it won’t, for instance, if the process you are trying to kill is running with higher privileges than the account you are running PowerShell with.You can use it like this. Here’s my list of `pwsh` processes:
    

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_001.jpg

图 A.1 – 一些 PowerShell 进程

让我们去掉 4052

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_002.jpg

图 A.2 – 更少的 PowerShell 进程

练习

  1. 输入 Get-Random

  2. 输入 Get-Random -Minimum 1 -Maximum 11

  3. 输入 Get-ChildItem -Path <文件夹路径>

  4. 输入 Get-ChildItem -Path <文件夹路径> -Recurse

  5. 输入 New-Item -Path <文件夹路径> -Name <项目名称> -ItemType

  6. 目录。

  7. 输入 Get-Uptime

  8. 输入 Out-File

  9. 存储一个用户名和密码,以便在 shell 或脚本中稍后使用。

  10. 这个 cmdlet 会将之前 cmdlet 或管道的输出转换为 HTML,然后可以在 web 浏览器中显示。请注意,你可能还需要使用 out-file 保存为文件,否则它只会在 shell 中显示 HTML 代码。

第二章

活动

  1. New 用于创建一个新对象。例如,New-Item C:\foo\bar.txt 将在 C:\foo 目录下创建一个名为 bar.txt 的空文本文件。Add 会将内容添加到现有对象中,因此 Add-Content C:\foo\bar.txt "PowerShell rocks my world" 会将该字符串添加到之前空的文本文件中。

  2. 只需像这样指定 -InputObject 参数:Get-Random -InputObject 20

  3. 使用 -Prefix 参数。这个参数在使用远程会话时特别有用。

练习

  1. Get-Content 是正确的。这个问题有点狡猾,因为 PowerShell 命令的批准动词 网页上指出,Get 应该保留用来获取有关对象的信息,而不是其内容。然而,Get-Content 是正确的,因为我们是获取文件的内容,并将其作为一个对象保留,以供将来使用,而不是从远程资源(如网页)读取数据。Read-Host 是一个读取数据的 cmdlet 示例——在这种情况下,它从 shell 中读取信息。

  2. 你应该看到屏幕上打印出 alive alive 这两个词。这是因为 ohOut-Host 的别名,它接收一个对象——在这种情况下是字符串 alive alive——并将其打印到默认的主机,通常是屏幕上。

  3. Get-ChildItem 有两个参数集。决定使用哪一组的参数是 -LiteralPath

  4. *.exe 被传递给 -filter 参数。如果查看帮助文件,你会看到 -filter 是一个位置参数,位置为 1,因此没有指定参数的第二个值将被理解为过滤器参数。

  5. 不行。-Filter 参数只能接受一个参数。如果你希望这个 cmdlet 执行,Get-ChildItem c:\foo\* -include *.exe, *.txt 会有效。

  6. Find-module *aws* 将查找许多由 Amazon 提供的用于与 AWS 工作的官方模块。

  7. 截至写作时,PowerShell Gallery 上没有,但 GitHub 上有一些。我不确定它们的官方性,使用时要小心。

  8. 按住 Ctrl 键并滚动鼠标滚轮是最简单的临时方式。Ctrl+(加号)或 Ctrl-(减号)也可以实现。如果要永久更改,请打开设置,点击你想要更改的配置文件,进入外观子部分。

第三章

活动

  1. 我们可以结合使用 -first-last-skip 来实现这一点,如下所示:

    -skip parameter will skip from the start, unless it is combined with -last. However, it is not positional, so if we specify both the -first and -last parameters, -skip will always skip from the start of the array; it doesn’t matter where we put it in the cmdlet:
    

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_003.jpg

图 A.3 – 使用 first、last 和 skip 时的输出

  1. 这是因为 -contains 不支持通配符。值必须完全匹配,除了它不区分大小写。

  2. Get-Command -ParameterName filter 会为我们完成此操作。如果你运行它,你会看到有很多这样的命令。它们大多数都使用我们在本章中看到的相同的 filter 块语法。

练习

  1. Get-Command 让我们可以找到 Get-Date cmdlet。接下来,我们需要使用 Get-Member 查看 Get-Date 返回的对象的属性。最后,我们需要使用 Select-Object 只显示 DayOfWeek 属性 – Day 返回月份中的天数。

  2. path 不是我们运行 Get-Process 时显示的属性,所以我们需要使用 Get-Member 来查找它。

  3. Get-Process | select-object name, cpu, path | sort-object path -descending

    简单。

  4. Where-Object 需要尽早使用。记住,过滤要从左侧开始

  5. 最好利用 cmdlet 的过滤属性,而不是将所有内容通过管道传递给 Where-Object,因此以下代码是最有效的方式:

    ForEach-Object here, like this:
    
    

    交互式的 ForEach-Object。

    
    
  6. Get-Content 是你需要的 cmdlet,我们将在第六章中详细讲解,PowerShell 与文件 – 读取、写入和操作数据。你会记得在本章之前的工作中,Get-Process-Name参数只接受ByProcessName,而不是对象ByValue,因此我们不能使用Get-Content来获取名称列表。相反,我们必须使用括号并将其直接传递给-Name参数。

  7. 这样无法运行;虽然 -computername 参数接受一个 system.string 对象,但它是通过 ByPropertyName 而不是 ByValue 方式进行的。正确的运行方式如下:

    shutdown command and can’t work against remote machines. Hopefully, you followed the instructions not to try it, as it will ignore the bobscomputer string and shut down your local machine if you don’t include the -WhatIf parameter.
    

第四章

活动

  1. 这完全是关于内存的。如前所述,栈的空间有限,值类型对象存储在栈中。因此,在尽量节省内存的同时,对编写代码的人透明是有意义的。[Int64] 类型的对象在栈上占用的空间是 [Int32] 类型对象的两倍。

  2. MyString 被告知获取 MyVariable 的内容,即整数 42,并将其作为字符串处理。随后,我们可以将一个整数放入 MyString,因为在创建时我们没有对其进行任何限制。

    反过来,用 [string]$MyOtherString,会将 MyOtherString 限制为只能包含字符串。

练习

  1. 里面有一个空格——空格是不允许的。如果我们真的必须在变量名中使用空格,那么必须将其括在大括号中——{My Variable},这比使用不包含空格的变量名可能更麻烦。

  2. System.Management.Automation.PSVariablePSVariable,甚至是 Variable

  3. 使用首选项变量 $ErrorView。默认情况下,它设置为 ConciseView,这是一个较短、便于阅读的消息,只包含错误信息。这个功能是在 PowerShell 7.0 中引入的,取代了稍显模糊的格式。我们仍然可以通过将 $ErrorView 设置为 NormalView 来查看旧格式。有趣的是,PowerShell 文档将 $ErrorView 列为首选项变量和自动变量——我怀疑这是错误的;我认为它是一个首选项变量。

  4. $Null 会这样做。$Null 与 0 不同,它仍然是一个值,只是一个空值。

  5. 我们可以使用 CompareTo() 方法。这将给我们三个可能的输出:如果整数相同,则为 0;如果第一个整数小于第二个,则为 -1;如果第一个整数大于第二个,则为 1。尝试以下操作:

    $x = 42
    $y = 23
    $y.CompareTo($x)
    

    这种用法将在下一章中派上用场,第五章PowerShell 控制流 – 条件语句与循环,我们将在其中探讨条件语句。

  6. 它是一个 System.Array 对象或数组。

  7. MyString.ToCharArray() 将把每个 char 输出为数组中的一个元素,并且每个元素占一行。

  8. 因为我们只使用了单引号,所以输出将是 My Name is $MyName。如果我们希望变量被展开,就必须使用双引号。

  9. 这是一个有序哈希表的 TypeName,我们可以使用 [ordered] 加速器来创建它。记住,这个放在语句的右侧,而不是左侧,像这样:

    $OrderedHash = [ordered]@{a=10;b=20;c=30}
    

第五章

活动

  1. 因为这个 switch7 { Write-Output 'contains 7' },它在查找一个整数,而它正在搜索的这一行是一个字符串,因此没有包含任何整数。如果我们把 switch 语句中的 7 替换为 '*7',使其成为一个字符串,那么它就能正常工作。

  2. 这是因为我们在输出之前执行了递增语句。如果交换这两个语句,便能证明当条件不满足时,脚本块并不会执行。

练习

  1. 没有。该语句只有在$x大于 4 时才会产生输出。这里没有else语句来提供替代的输出。这是一个真正的条件语句,而不是提供替代流程的条件语句。

  2. $x = 4 ; IF ($x -gt 4) {Write-Host '$x is larger than 4'}Else {Write-Host '$x is not larger than 4'}

  3. $x = 4 ; IF ($x -gt 4) {Write-Host '$x is larger than 4'}elseif ($x -lt 4) {Write-Host '$x is smaller than 4'} Else {Write-Host '$x is 4'}

  4. $x = 4 ; ($x -gt 4) ? (Write-Host '$x is larger than 4') : (Write-Host '$x is not larger than 4')

  5. 由于foreach位于管道字符之后,这里只有一个语句,并且foreach被解释为ForEach-Object的别名,因此语法是错误的。我们可以通过将管道字符替换为分号来纠正它。这将其分为两个语句,foreach被正确地解释:

    $processes = Get-process ; foreach ($process in $processes) {$process.name}
    
  6. number = 0 ; Do {$number ++ ; Write-Host "Number is $number"} While (!($number -eq 5))将能正常工作。我们会看到使用not运算符别名(!)来反转语句的结构会很常见。

  7. 它缺少迭代器。这样写就能正常工作:for ($i = 0 ; $i -lt 5 ; $i ++) {``Write-Host $i}.

  8. switch语句。将它们用于循环外部或switch语句中可能会导致不可预测的结果。

  9. 有几种方法可以实现这个;任何有效的方式都是对的,但我的解决方案是用for循环替换现有的while循环来实现计数器。我还添加了一个额外的elseif语句来处理胜利条件,像这样:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_004.jpg

图 A.4 – 猜限量布鲁西

网络上有很多写法——我选择了这种方式,主要是使用了我们在本章中介绍的概念。

第六章

活动

这只是实现的方法之一。如果你有其他有效的做法,恭喜你。那也是正确的方式——至少是其中之一。根据编程风格练习,至少有 41 种其他方法:

$TheTrial = Get-Content -Path .\thetrial.txt -Raw
$StopWords = Get-Content -Path .\stopwords.txt -Raw
$TrialWords = $TheTrial.Split(" ", "`t", "`n", ",","`"",".", [System. StringSplitOptions]::RemoveEmptyEntries)
$Words = [System.Collections.ArrayList]@()
Foreach ($Word in $TrialWords) {
$LWord = $Word.ToLower()
if (!($StopWords.Contains($LWord))) {
$Words.Add($Word)
}
}
$Grouped = ($Words | Group-Object | Sort-Object Count)
$Grouped[-1 .. -10]

这里是运行的结果:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_005.jpg

图 A.5 – 《审判》英文版中最常见的十个单词

让我们快速地回顾一下:

  • 第 1 行和第 2 行使用Get-Content将我们的两个文件导入 PowerShell,以Raw格式作为单一字符串。

  • 第 3 行添加了一些额外的分隔符并移除了空字符串。我不指望你了解字符串拆分选项,所以我在提示中给了你这个信息。

  • 第 5 行创建了一个空的数组列表来存储有趣的单词;如果我们使用 PowerShell 数组,这将非常缓慢。

  • 第 7 行开始了一个Foreach循环,遍历$TrialWords中的每个单词。

  • 行 8 创建了一个变量,并且每次循环重复时,都将每个单词转换为小写。

  • 行 9 开始一个 if 语句,匹配条件“$Lword 不在 $StopWords 中”。请注意,我们使用的是 -Contains 方法,它匹配单个字符串中的子字符串,因此它在 $StopWords 字符串中搜索与 $LWord 匹配的子字符串。

  • 如果条件为真,行 10 会将 $Word 添加到 $Words 数组列表中。

  • 行 13 将 $Words 中的单词分组并排序。

  • 行 14 返回前 10 个最常见的单词,按降序排列。

练习

  1. **Get-Childitem -Path C:\Temp -File | Format-Wide -**Column 3

  2. **Get-Process | Format-Wide -column 5 | Where-Object id -**gt 100

    它将不会产生任何输出。记住,格式正确。正确的代码应该如下:

    Get-Process | Where-Object id -gt 100 | Format-Wide -column 5
    
  3. **“我爱 PowerShell” | Out-File -**Path Q3.txt

  4. “Sooo much” | Out-File -Path Q3.txt -Append

  5. **Get-ChildItem | Export-Csv -Path items.csv -**Delimiter “;”

  6. **(**Get-ChildItem 函数:).count

  7. Get-Content Q3.txt -Delimiter “ “(Get-Content Q3.txt).Split(“ “)

  8. PSCustomObjects

  9. Import-Clixml: 找不到命名空间名称为 ‘http:// schemas.microsoft.com/powershell/2004/04’ 的元素 ‘Objs’。

    我们尝试导入的 XML 文件格式不正确,无法被 cmdlet 识别,或者它不是 PowerShell 对象。

第七章

活动

这是我的解决方案:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_006.jpg

图 A.6 – 一个解决方案

行 2 从 API 获取 JSON 格式的数据,并将其放入一个变量中。如果我们查看 $astronauts 变量,我们可以看到它有两个键值对,messagepeoplepeople 包含一个 JSON 对象数组,这些对象本身有两个键值对;namecraft。我们可以在下图中看到这一点:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_007.jpg

图 A.7 – 获取 JSON 数据

因此,我们知道所需的数据在 $astronauts.people.name 键值对中。现在我们只需要以一种愉悦的方式显示它。我们在 第六章 中讨论了如何使用 ConvertTo-HtmlPowerShell 和文件 - 读取、写入与处理数据,这是我选择的方式。

在第 5 行,我们设置了一个 $params 哈希表,这样就可以展开我们需要的所有参数。我选择包括一个 CSS 样式表来显示一张美丽的图片,但这是不必要的。这是我的 CSS:

Table {
color: white;
text-align: left;
background-color: black;
}
Body {
background-image: url("iss.jpg");
background-size: cover;
background-repeat: no-repeat;
background-color: black;
font-family: 'Trebuchet MS';
color: yellow;
}

最后,魔法发生在第 12 行。我们通过管道将感兴趣的值传递给 ConvertTo- Html,然后使用 Out-File 将 HTML 写入文件。之后,我们可以在我们选择的浏览器中打开此文件。

希望这已经向你展示了使用 API 获取、处理和显示数据是多么简单。

练习

  1. **Invoke-WebRequest -Uri ‘https://httpbin.org/delete’ -**Method Delete

  2. 我们在第一次请求(通常是登录请求)中使用-SessionVariable参数传递一个字符串,然后在后续的请求中,我们使用-WebSession参数作为变量传递会话变量。

  3. 我们可以使用-SkipCertificateCheck参数,但只有在我们确定该站点有效且不具恶意时才应该这么做。

  4. 我们犯的错误是在通过请求头提供令牌之前对其进行编码。应该将令牌编码为参数并以明文形式提供给请求头。这意味着通过参数传递稍微更安全,但并不是所有服务都会接受这种方式。

  5. WebSocket API 通常是有状态的。这意味着关于请求者的信息会在多个请求之间持续存在;这使得使用起来更加复杂,因为我们需要持续保存会话信息,而且也容易受到网络条件的影响。

  6. 有很多方法可以做到这一点,但最直接的方法可能就是这个:

    (Invoke-RestMethod 'http://universities. hipolabs.com/search?country=United+kingdom').name | Where-Object {$_ -like '*x*'} | Measure | select -Property 'count'.I get `8`, but this is subject to change.
    
  7. 我们可以通过-Schema参数传递一个描述自定义模式的长字符串,或者我们可以通过-SchemaFile传递一个文件位置。你说得对!这一点没有在章节中提到。希望你通过阅读帮助文件找到了答案。

第八章

活动

  1. 做这件事的简单方法是在Param()块中创建一个$Output变量,然后将其作为参数传递给脚本中的Out-File cmdlet,像这样:

    [CmdletBinding()]
    Param(
    $City = "London",
    $Output = "c:\temp\poshbook\ch8\WeatherData.html"
    )
    $headers = @{"key" = "<Key>"}
    $uri = "https://api.weatherapi.com/v1/current.json?q=$($City)&aqi=no"
    $response = Invoke-RestMethod -Uri $uri -Method GET -Headers $headers
    $response | Convertto-Html | Out-File $Output
    

    这样做的问题是,如果我们想更改文件名,每次都需要输入文件名和路径。我们更有可能更频繁地更改文件名,而不是更改路径。让我们像这样将路径和文件名分开:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_008.jpg

图 A.8 – 分离文件名和文件路径

现在,我们只需在需要时传递一个不同的文件名,或者在需要时传递一个不同的路径,而不必每次都输入整个内容。

  1. 从一个具有可以接收多个字符串的参数的 cmdlet 帮助文件中,我们可以看到该属性包含一对空的方括号,像这样:[string[]]。这一部分相对简单。我们还需要处理传递给-City参数的数组中的每个元素。为了做到这一点,我们还需要一个foreach循环,将其包裹在脚本的工作部分周围。

    最后,我们需要某种方式将每个城市的数据发送到单独的输出文件。我选择通过传递foreach循环中的$item变量来实现。以下是我的解决方案。你的方案可能会有所不同,但只要它能工作,那就太好了。

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_009.jpg

图 A.9 – 处理多个城市

在第3行,我已将[string[]]属性添加到-City参数中,以允许它接收多个字符串。

我在第9行打开了一个foreach循环,并在第22行关闭。中间的行现在将为$City参数中包含的每个字符串$item重复。我还缩进了中间的行,以便更容易阅读。

我已将第17行更改为使用$item变量(当前城市),而不是$City中的数组,因为那样会导致错误——API 一次只接受一个字符串。

最后,我更改了第11行,使其将每个输出发送到一个包含城市名称的文件中。

  1. 这是我的示例。你的可能不同,但希望你提前包含了关于需要 API 密钥的警告:

    <#
    .SYNOPSIS
    Gathers weather data for a number of cities and stores the API output.
    .DESCRIPTION
    This cmdlet will gather the current weather data from a number of cities from
    the API endpoint at https://api.weatherapi.com and outputs the responses to a
    set of named HTML files stored in the specified directory.
    The -City parameter takes an array of strings, either explicitly or via the
    pipeline (ByValue).
    The -OutputFile parameter takes a single string specifying the filename and
    suffix. This filename will be prefixed by the string provided in the -City
    parameter, eg. London_WeatherData.html
    The -OutptPath parameter specifies a location for the output file.
    The -Key parameter specifies a txt file that contains the key from
    weatherapi.com
    .NOTES
    This script requires a personal API key from https://weatherapi. com
    The output path will need to exist before running the script
    .LINK
    No link, sorry.
    .EXAMPLE
    .\weatherdata.ps1 -City london,paris
    This will generate two html files; one for London and one for Paris
    #>
    

练习

  1. 这可能是由于一些设置问题,但让我们假设它是最简单的情况:执行策略对你来说是正确的,但对他们来说不正确。这意味着CurrentUser策略在限制他们。

    使用以下方法应该可以,如果脚本是在本地机器上编写的,或者如果我们在另一台机器上使用代码签名证书签名过:

    Unrestricted.
    
  2. -Maximum 参数。我没有见过任何龙与地下城的骰子是从 1 以外的数字开始的(除了 d100,但稍后我们会在一个问题中讨论它)。

  3. 嗯,我们可以有几种方法来做到这一点,但希望你能想出类似这样的解决方案:

    [CmdletBinding()]
    param(
    $Sides = 20
    )
    get-random -minimum 1 -Maximum $Sides
    

    你不需要在其中包含CmdletBinding()属性,但我总是会加上。

  4. 它应该是一个整数,我们会用[``int]属性来指定:

    [int]$Sides = 20
    
  5. 所以,如果我们阅读这个链接,我们可以看到我们可以将ValidateSet属性分配给一个参数,并传递一个合法值的数组,它看起来是这样的:

    [CmdletBinding()]
    param(
    [ValidateSet(4,6,8,10,12,20)]
    [int]$Sides = 20
    )
    get-random -minimum 1 -Maximum $Sides
    
  6. 为了做到这一点,我们需要一个循环,并通过参数指定运行循环的次数,然后将每次循环的输出添加到累积总和中。它可能像这样:

    [CmdletBinding()]
    param(
    [ValidateSet(4,6,8,10,12,20)]
    [int]$Sides = 20,
    [int]$Dice
    )
    $total = 0
    while ($Dice -gt 0) {
    $result = (Get-Random -Minimum 1 -Maximum $Sides)
    $Dice -= 1
    $total += $result
    write-output "die says $result"
    }
    Write-Output "The total is $total"
    
  7. 这是因为没有为$Dice分配默认值。我们可以为它分配一个默认值,但更好的方法可能是通过添加[Parameter(Mandatory)]使其成为一个必需的参数,如下所示:

    param(
    [ValidateSet(4,6,8,10,12,20)]
    [int]$Sides = 20,
    [Parameter(Mandatory)]
    [int]$Dice
    )
    
  8. 我们可以使两个参数都成为必需参数,并包括一个HelpMessage属性,解释每个参数需要输入什么。

  9. 所以,第一件事是将100的值添加到$``sides参数的ValidateSet属性中。

    完成后,我们需要以不同的方式处理100的值,所以不能只是将它添加到循环中。我使用了ifelse语句。以下是我的最终脚本:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_010.jpg

图 A.10 – 我的 15 级圣武士会击败你的混乱邪恶牧师

记住,有很多方法可以做到这一点;如果你的代码与我的完全不同,但它能正常工作,那也没问题。

第九章

活动

  1. 没有任何效果,因为我们的参数没有写成接受管道输入。正如我们在 第八章 中发现的,编写我们的第一个脚本 – 将简单的 Cmdlet 转化为可重用的代码,要使参数接受管道输入,我们必须添加一个参数,如下所示:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_011.jpg

图 A.11 – 从管道接收值

在第 9 行,我们为参数添加了 ValueFromPipeline 参数,这样它就可以接受来自管道的值。我们还将函数包含在一个 process 块中,在第 12 行打开,第 20 行关闭;如果没有 process 块,函数将仅作用于管道中的最后一个值。

  1. 因为 Get-Random 只接受一个位置参数,-Maximum。如果我们像之前那样运行,那么最大值将被设置为 15,而 cmdlet 不知道如何处理 20 这个值。同样,Get-Fifteen20 15 -maximum 20 也无法正常工作,因为 -Maximum 参数已经由命名值 20 填充,所以它不知道如何处理 15 这个值。然而,Get-Fifteen20 -minimum 15 20 是可以工作的。

  2. 这总是有很多方法可以做。我的方法如下:

    function Remove-Log {
    $limit = (Get-Date).AddDays(-7)
    Get-ChildItem -Path "C:\temp" -Include "MyLogFile*" -Recurse -Force |
    Where-Object {$_.CreationTime -lt $limit} |
    Remove-Item -Force
    }
    

    我创建了一个名为 Remove-Log 的函数, 我可以在脚本中调用它。我创建了一个名为 $limit 的变量,它获取运行时日期前七天的日期。然后我使用通配符从 C:\temp 目录中获取所有以 MyLogFile 开头的项。接着我使用 Where-Object 对列表进行过滤,只选择早于 $limit 日期的文件。最后,我将其管道传输到 Remove-Item,并使用 -Force 参数来抑制任何确认。

练习

  1. 避免草率的抽象化 – 这是一个软件工程原则,鼓励我们仅在知道需要它并且确切知道它需要做什么时才创建抽象,例如函数。

  2. 因为点源会导致被调用的内容在本地或父作用域中运行,而不是在适当的子作用域中运行。

  3. 因为仅仅调用变量会产生脚本块中的代码;但它不会执行。我们需要使用 invoke() 方法、调用操作符或 Invoke-Command cmdlet。我们不应该在没有仔细考虑的情况下点源它。

  4. ValidatePattern 验证属性应该能解决这个问题,但我们需要使用正则表达式。唉!希望你已经查阅了 进一步阅读 部分中提到的帮助文件。

  5. 因为过滤器期望管道输入,而我们没有提供任何输入。然而,365 | get-square 将是有效的。

  6. 我们正在防止 $number 变量从另一个作用域中访问。

  7. 函数是有名字的,而脚本块是匿名的。

  8. 我们正尝试通过管道传递一个值,但没有参数接受管道输入。我们需要将其设置为高级函数并创建一个接受管道输入的参数,或者我们需要使用 $Args

  9. 我们将按如下方式编写函数:

    Function get-root($a) {
    <what goes here?>
    }
    [math]::Sqrt($a)
    

    我们可以这样使用它:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_012.jpg

图 A.12 – 简单获取根的方式

第十章

活动

  1. -ErrorAction 参数将覆盖 $ErrorActionPreference 变量,而 nosuchfile 字符串将导致一个终止错误。由于这是一个终止错误,cmdlet 将不会处理 bar.txt

  2. 因为如果出现错误,错误对象会被放入管道中,替代导致错误的字符串。

练习

  1. 终止错误会完全停止脚本执行。非终止错误可能会停止脚本执行当前步骤,但 PowerShell 会继续执行脚本的下一步。

  2. 可以使用 Get-Error 命令,它会显示最近的错误对象,或者使用 $Error 变量。这个变量包含了会话期间创建的所有错误对象,默认最大数量为 256。

  3. -ErrorActionPreference 变量允许我们为特定 PowerShell 会话中运行的所有 cmdlet 和脚本设置默认的错误处理偏好。它决定了错误是否应该显示、忽略,或者以特定方式处理。

  4. Write-Error cmdlet 允许我们手动生成并在脚本中显示自定义的非终止错误消息。当我们希望明确向用户或调用代码信号错误状态时,它非常有用。

  5. 生成一个终止错误,这个错误可以通过 try/catch 语句对进行处理。

  6. 可以通过在 cmdlet 或高级脚本中使用 -Debug 参数,或者将 $DebugPreference 变量设置为 Continue 来启用调试;默认值是 SilentlyContinue

  7. 通过在脚本中使用 Write-Debug cmdlet。调试消息是为编写代码的人提供的;错误消息是为使用代码的人提供的。因此,调试消息应该包含有关脚本当前状态的详细信息,可能包括变量值和步骤计数。

  8. 断点是设置在脚本中的一个标记,用于在特定行或条件下暂停脚本执行。它允许我们检查该点时脚本和变量的状态。网球中的断点与此完全不同。

  9. 它执行脚本的当前行并继续到下一行,但会把整个函数当作一行来执行。所以,如果下一行代码是一个函数,或者我们当前在一个函数中,那么整个函数调用会完成,而不是继续执行函数中的下一行。

第十一章

活动

尝试运行 Remove-Module 来移除我们刚刚安装的模块,然后运行 Get-Square。会发生什么?为什么会发生这种情况?

Get-Square cmdlet 仍然可用。这是因为我们正确地将模块保存在模块路径中;这意味着当我们调用模块中的函数时,PowerShell 会自动加载该模块。我们可以在以下截图中看到它的工作原理:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_013.jpg

图 A.13 – 使用 PowerShell 自动加载

在第一行,我列出了加载的模块。然后,我运行 Get-Square 7,这将自动加载 MyFirstModule。之后,我通过再次运行 Get-Module 确认了这一点。我们可以看到,通过在最后一行使用 -Verbose 开关运行 Remove-Module,它移除了 Get-Square cmdlet。

练习

  1. Get-Module

  2. 它将模块导入到全局作用域。请注意,当我们从命令提示符导入模块时,它已经被导入到全局作用域——我们在从另一个模块内部导入模块时会使用这一点;这就是嵌套模块的情况。

  3. 我们需要在 Import-Module-Name 参数中提供模块的完整路径。

  4. 如果它们不是我们要使用的函数,可以使用 -NoClobber 参数,或者如果是我们要使用的函数,可以使用 -Prefix 参数。

  5. 可以通过模块文件中的 Export-ModuleMembers cmdlet 或使用模块清单来实现。

  6. 它提供了一个指向模块在线文档的链接,允许帮助文档得到更新。

  7. 在模块的上下文中,它可能是函数或 cmdlet 输出的格式化信息,或者是自定义类型信息。

  8. 我们将获取 cmdlet;.dll 扩展名表示二进制模块,因此其中的命令将属于 Cmdlet 类型。脚本模块包含 Function 类型的命令。

  9. 因为它们很慢。CDXML 会被解析成 PowerShell 脚本,然后该脚本需要被解释。

第十二章

练习

  1. New-PSSession 创建一个持久会话,但可以使用 Enter-PSSession 创建一个临时会话。

  2. 通过使用 SSH。

  3. AllSigned 执行策略只允许运行由受信发布者签名的脚本。

  4. -ExecutionPolicy Bypass 开关用于在运行脚本时暂时绕过执行策略。

  5. PowerShell AMSI。

  6. 受限语言模式用于限制访问 PowerShell 中的危险 cmdlet 和脚本功能。

  7. 通过使用 JEA。

  8. 脚本块日志记录会记录在 PowerShell 中执行的脚本块的内容,从而提供对潜在恶意操作的可视性。它将内容记录在 Windows 的 PowerShellCore 操作事件日志中,在 Linux 中则记录在 systemd 日志中。

  9. 安全字符串是类型为 System.Security.SecureString 的 PowerShell 对象,已加密字符串是已使用密钥加密的字符串对象,因此无法在文件中读取。

第十三章

活动

  1. 我们可以用Invoke-CimMethod编写一个相当复杂的 cmdlet,但这不是最好的方法。相反,我们应该使用专门为此目的编写的 cmdlet,Stop-Process,并指定notepad进程的ProcessId

    Invoke-CimMethod like this:
    

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_014.jpg

图 A.14 – 使用 CIM 命令设置默认打印机

在第一个命令中,我将打印机对象放入一个变量中,然后使用该变量作为Invoke-CimMethod的输入对象,并调用SetDefaultPrinter方法。返回值0表示成功。

请注意,SetDefaultPrinter方法没有出现在$printer变量中。不幸的是,我们需要阅读文档来发现这个方法:

练习

  1. 显示模块。

  2. 它可能是在 PowerShell Core 发布之前编写的,作者可能没有在清单中包含兼容性信息,或者它可能不是一个清单模块。

  3. Windows PowerShell 5.1。

  4. 反序列化的对象。

  5. 使用- UseWindowsPowershell参数。

  6. __NAMESPACE

  7. 它允许我们在查询远程机器时指定不同的超时时间,因为默认情况下每台机器的超时时间是 3 分钟。

  8. 我们可能可以使用Set-CimInstance,但由于许多属性是不可写的,我们更可能会使用Invoke-CimMethod

  9. 我们将它们作为Dictionary哈希表传递给- Arguments参数。

第十四章

活动

由于 PowerShell 对于文件路径分隔符非常宽容,唯一需要根据平台不同而不同的代码部分是如何获取机器名称。其他部分非常简单。这是我的解决方案;你的解决方案可能会有很大不同,但仍能完成任务:

if ($IsWindows) {
$computername = $env:COMPUTERNAME
}
elseif ($IsLinux) {
$computername = (hostname)
}
Get-Process |
Sort-Object -Property CPU -Descending |
Select-Object -First 5 |
Out-File "$($computername)_processes.txt"

因为我们需要使用不同的方法来获取机器名称,所以这两行代码位于if语句中。其他部分在 Linux 和 Windows 上工作相同,所以非常简单。这是在我的 CentOS 机器上运行时的样子:

https://github.com/OpenDocCN/freelearn-linux-pt3-zh/raw/master/docs/pwsh7-ws/img/B17600_Assessments_015.jpg

图 A.15 – 在 CentOS 上运行跨平台脚本

如我们所见,它运行得很好。这个脚本可以加入一些错误检查;例如,如果两个自动变量都是false,该怎么办?

练习

  1. 互联网。与大多数 Linux 发行版一样,Kali Linux 不受 Microsoft 支持。

  2. 诱导性问题。实际上,我们在 Linux 上调用的是ls Bash 命令。当我们在 Windows 上输入它时,实际上是通过ls别名调用Get-ChildItem

  3. 无论是\还是/,都可以,幸运的是。这意味着编写跨平台脚本变得更加容易。

  4. 通过调用$IsMacOS变量。如果返回true,那么我们就在 macOS 上运行。

  5. 通过使用sudo pwsh命令启动 PowerShell。PowerShell 会话中没有提升权限的方式。

  6. 使用带有-``HostName参数的New-PSSession

  7. 使用带有-``KeyFilePath参数的New-PSSession

  8. 这是一个文件传输程序,包含在 PowerShell 7 的安装包中。

  9. Ed25519 是一种基于公钥/私钥的数字签名加密算法,较新且更安全。

第十五章

答案

  1. 因为 Zero 和 Pico 使用 ARMv6 芯片架构,这与.NET 不兼容,因为.NET 要求 ARMv7 或 ARMv8 架构。

  2. 无论是C:\Users\<用户名>\.sshconfig还是C:\ProgramData\ssh\ssh_config

  3. Test-NetConnection

  4. ssh <用户名>@<主机名>

  5. sudo bash ./install.sh

  6. 因为每次都输入~/powershell/pwsh很麻烦。

  7. **sudo ln -s ~/**powershell/pwsh /usr/bin/pwsh

  8. 使用带有-Raw开关参数的Get-GpioPin cmdlet。

  9. Debian Linux,类似于 Ubuntu。

第十六章

练习

  1. **$object = New-Object -**TypeName Namespace.ClassName

  2. **Add-Type -**AssemblyName “AssemblyName”

  3. **[**Namespace.ClassName]::MethodName()

  4. **[**Namespace.ClassName]::PropertyName

  5. $object = New-Object -TypeName Namespace.ClassName -ArgumentList (arg1, arg2, …)

  6. **Add-Type -**Path “Path\To\Assembly.dll”

  7. 使用 $object.GetType()

  8. **$object | Get-Member -**MemberType Method

  9. **$**object.MethodName()

  10. **$**object.PropertyName

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值