Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第九章:安全与最佳实践

欢迎来到 PowerShell 精通之路的最后一章。在本章中,我们将从追求“功能实现”的技术层面,上升到追求“专业、安全、高效、可维护”的工程化和职业化层面。一个卓越的 PowerShell 专家,不仅要能编写出解决问题的代码,更要能编写出让同事信赖、让系统安全、让未来可期的代码。本章将聚焦于 PowerShell 的安全体系、编码的最佳实践以及现代化的版本控制,为你的 PowerShell 技能树点亮最后,也是最闪亮的几颗星。


9.1. PowerShell 安全深度解析

PowerShell 自诞生之日起,就身处安全攻防的风暴中心。它强大的能力使其成为系统管理员的得力助手,也同样吸引了攻击者的目光。因此,微软在 PowerShell 中内置了多层次、纵深防御的安全特性。理解这些特性,并正确地使用它们,是每一个负责任的 PowerShell 使用者的必修课。

9.1.1. 执行策略的真相:它不是一个安全边界,而是一个安全带

**执行策略(Execution Policy)**是 PowerShell 新手遇到的第一个,也是最常被误解的安全特性。

  • 它的作用是什么? 执行策略的唯一目的,是防止用户无意中运行了不可信的脚本。它就像汽车上的安全带,旨在防止因意外或疏忽造成的伤害,但它无法阻止一个执意要解开它、猛踩油门的司机。

  • 常见的策略级别:

    • Restricted:默认策略。不允许运行任何脚本文件。
    • AllSigned:只允许运行由受信任的发布者签名的脚本。
    • RemoteSigned:允许运行本地创建的脚本;对于从网络下载的脚本,则必须由受信任的发布者签名。这是服务器环境中最推荐的策略。
    • Unrestricted:允许运行所有脚本,但在运行从网络下载的未签名脚本时会提示用户。
    • Bypass:什么都不阻止,什么都不提示。
  • 它为什么不是一个安全边界? 执行策略只对 powershell.exe 直接运行 .ps1 文件生效。有无数种方法可以轻松地绕过它,例如:

    powershell

    # 将脚本内容通过管道传递给 powershell.exe
    Get-Content .\MyScript.ps1 | powershell.exe -noprofile -
     
    # 使用 -EncodedCommand 参数
    $command = Get-Content .\MyScript.ps1
    $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
    $encodedCommand = [Convert]::ToBase64String($bytes)
    powershell.exe -EncodedCommand $encodedCommand
    

    结论:永远不要依赖执行策略来保护你的系统免受恶意脚本的攻击。它只是一个防止意外操作的“安全带”,而不是一个坚不可摧的“防火墙”。真正的安全,需要依赖下面将要介绍的更强大的机制。

9.1.2. 代码签名:为你的脚本提供身份和完整性保证

如果执行策略是“安全带”,那么**代码签名(Code Signing)**就是脚本的“数字身份证”和“防伪封条”。它通过公钥加密技术,为你的脚本提供了两个至关重要的安全保证:

  1. 身份验证(Authentication):确认脚本的作者是谁,且该作者是可信的。
  2. 完整性(Integrity):保证脚本从签名那一刻起,内容没有被任何人篡改过。哪怕只修改了一个空格,签名也会立即失效。

工作流程:

  1. 获取代码签名证书:你可以从公共证书颁发机构(CA)购买,或者在企业内部搭建自己的 PKI 环境来颁发。

  2. 签名脚本:使用 Set-AuthenticodeSignature Cmdlet,将你的证书应用到 .ps1 或 .psm1 文件上。

    powershell

    # 获取你的证书
    $cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert
     
    # 对脚本进行签名
    Set-AuthenticodeSignature -FilePath ".\My-Critical-Script.ps1" -Certificate $cert
    
  3. 部署与执行:将签名的脚本分发到目标计算机。在执行策略为 AllSigned 或 RemoteSigned 的环境下,用户可以无缝地运行这个脚本,因为系统能够验证其来源可信且内容完整。

在企业环境中,对所有生产环境的自动化脚本进行强制代码签名,是一项至关重要的安全最佳实践。

9.1.3. JEA (Just Enough Administration):实现最小权限委托管理的艺术

JEA (Just Enough Administration) 是 PowerShell 中一项革命性的安全技术,它是**最小权限原则(Principle of Least Privilege)**的完美体现。

核心思想: 在传统模式下,如果一个初级管理员需要重启一项服务,你可能需要给予他服务器的本地管理员权限,但这同时也赋予了他执行其他所有管理操作的潜在能力,风险极高。 而 JEA 允许你创建一个受限的、基于角色的管理端点(Endpoint)。当用户通过 PowerShell Remoting 连接到这个 JEA 端点时,他们不再拥有自己的全部权限,而是只能执行你预先精确定义好的一小部分命令,并且这些命令在后台是以一个高权限的虚拟账户身份运行的。

示例:创建一个只允许重启网站服务和查看日志的 JEA 角色

  1. 创建角色能力文件 (.psrc):定义允许执行的命令。

    powershell

    # WebAdmin.psrc
    @{
        VisibleCmdlets = 'Get-Website', 'Restart-Website', 'Get-LogContent' # 假设这些是你自己写的函数
        # 甚至可以限制参数
        VisibleFunctions = @{ Name = 'Restart-Website'; Parameters = @{ Name = 'Name'; ValidateSet = 'Default Web Site', 'AdminPortal' } }
    }
    
  2. 创建会话配置文件 (.pssc):将角色映射到用户或组,并指定运行身份。

    powershell

    # WebAdminSession.pssc
    @{
        SessionType = 'RestrictedRemoteServer'
        RunAsVirtualAccount = $true
        RoleDefinitions = @{
            'CONTOSO\WebAppAdmins' = @{ RoleCapabilities = 'WebAdmin' }
        }
    }
    
  3. 注册 JEA 端点Register-PSSessionConfiguration -Name "WebAdminJEA" -Path ".\WebAdminSession.pssc"

现在,当 CONTOSO\WebAppAdmins 组的成员连接时,他们必须指定这个端点: Enter-PSSession -ComputerName "SRV01" -ConfigurationName "WebAdminJEA" 在这个会话中,他们输入 Get-Command,会发现只能看到 Get-Website 等寥寥几个被授权的命令。他们成功地重启了网站,但完全无法执行任何其他危险操作。

JEA 是一种强大的、精细化的权限委托机制,它让你可以在不授予用户高权限的情况下,安全地将日常运维任务委托出去,是提升企业运维安全性的终极武器。

9.1.4. 日志记录与审计:利用脚本块日志和模块日志来追踪 PowerShell 活动

如果说前面的机制是“预防”,那么**日志记录(Logging)**就是“检测”和“响应”的基石。当安全事件发生后,详尽的日志是进行事后追溯和取证分析的唯一依据。PowerShell 提供了极其强大的日志记录能力。

你需要通过组策略(Group Policy)或直接修改注册表来启用以下两种最重要的日志:

  1. 模块日志记录 (Module Logging)

    • 作用:记录特定模块中 Cmdlet 的执行情况。
    • 配置:你可以指定要监控的模块名称(如 ActiveDirectoryServerManager)。当这些模块中的任何命令被执行时,PowerShell 会记录下完整的命令、参数和执行者信息。
    • 日志位置Windows Logs -> PowerShell 事件日志,事件 ID 4103。
  2. 脚本块日志记录 (Script Block Logging)

    • 作用:这是最强大的日志功能。它会记录所有在系统上被处理的 PowerShell 脚本块的内容。无论是直接在控制台输入的命令,还是执行的 .ps1 文件,甚至是动态生成的、混淆过的恶意代码,只要它被 PowerShell 引擎执行,其原始的、解密后的内容就会被记录下来。
    • 配置:强烈建议在所有关键服务器和工作站上启用此功能。
    • 日志位置Applications and Services Logs -> Microsoft -> Windows -> PowerShell -> Operational 事件日志,事件 ID 4104。

脚本块日志记录是检测无文件攻击、内存中执行的恶意 PowerShell 代码的“天眼”。它让攻击者在 PowerShell 世界中的一举一动都无所遁形。将这些日志集中收集到 SIEM (安全信息和事件管理) 系统中进行分析,是现代企业安全监控体系的关键一环。


通过对 PowerShell 安全体系的深度解析,我们学会了如何像一位安全架构师一样,构建一个纵深防御的自动化环境。我们明白了执行策略的局限,掌握了代码签名的价值,领略了 JEA 的精妙,并认识到了日志审计的不可或-缺。将这些安全实践融入到你的日常工作中,你手中的 PowerShell,将永远是一股建设性的、值得信赖的、守护系统的强大力量。


9.2. 编写高效、可读的 PowerShell 代码

在上一节中,我们为我们的 PowerShell 王国构建了坚固的“城墙”和灵敏的“岗哨”,学会了如何从安全专家的角度来审视和保护我们的工作成果。现在,我们要将目光从外部的防御,转向内部的“城市建设”。

一个伟大的王国,不仅要有强大的防御,更要有优美的建筑、通畅的道路和高效的工匠。同样,一个卓越的 PowerShell 专家,不仅要能实现功能,更要能编写出“优美”的代码。这种“优美”,意味着代码不仅能正确工作,还易于阅读、便于协作、运行高效、经得起时间的考验。

本节,我们将学习成为一名 PowerShell 的“软件工匠”。我们将学习社区公认的“建筑规范”(风格指南),探索提升“物流效率”(性能优化)的技巧,并掌握为我们的作品撰写清晰“说明书”(注释)的艺术。这不仅关乎个人技艺的提升,更体现了一名专业工程师的素养与对团队的责任感。

代码是写给人读的,只是偶尔让计算机执行一下。这句软件工程领域的名言,在 PowerShell 的世界里同样适用。随着你的脚本变得越来越复杂,或者当你开始与团队成员协作时,代码的质量——它的可读性、一致性和性能——就变得与它的功能本身同等重要。本节将为你提供一套经过社区千锤百炼的最佳实践,帮助你编写出既强大又优雅的专业级 PowerShell 代码。

9.2.1. 社区风格指南:遵循 PSScriptAnalyzer 的建议,编写规范的代码

在任何一门成熟的编程语言中,社区都会逐渐形成一套广为接受的编码风格指南,以确保代码的一致性和可读性。PowerShell 也不例外。幸运的是,我们不需要去死记硬背这些规则,因为我们有一个强大的自动化工具——PSScriptAnalyzer

PSScriptAnalyzer 是一个由 PowerShell 团队官方发布的静态代码分析工具。它会检查你的脚本,并根据社区的最佳实践规则集,给出改进建议、警告甚至错误。它就像一位时刻在你身边、经验丰富的代码审查专家。

  • 安装与使用

    powershell

    # 从 PowerShell Gallery 安装
    Install-Module -Name PSScriptAnalyzer -Scope CurrentUser
     
    # 对单个脚本文件进行分析
    Invoke-ScriptAnalyzer -Path ".\My-Script.ps1"
     
    # 对整个模块目录进行递归分析
    Invoke-ScriptAnalyzer -Path ".\MyModule\" -Recurse
    
  • 它能帮你发现什么?

    • 使用未声明的变量:避免因拼写错误导致的逻辑错误。
    • 使用 Cmdlet 别名:在脚本中应使用完整的 Cmdlet 名称(如 Get-ChildItem 而不是 ls),以增强可读性。别名只推荐在交互式控制台中使用。
    • 硬编码的路径或凭据:提醒你将这些值参数化,以提高脚本的通用性和安全性。
    • 不规范的大小写:建议 Cmdlet 和参数使用 PascalCase,变量使用 camelCase 或 PascalCase
    • 未使用的变量:帮助你清理冗余代码。
    • 以及更多…
  • 与 VS Code 集成: 当你安装了 PowerShell 扩展后,VS Code 会自动集成 PSScriptAnalyzer。它会在你编写代码时,实时地在问题代码下画出绿色的波浪线,并将鼠标悬停在上面时,给出详细的修改建议。

最佳实践:将 PSScriptAnalyzer 作为你开发流程中不可或缺的一环。在提交代码前运行一次分析,并解决所有问题。这能极大地提升你的代码质量,并让你养成良好的编码习惯。

9.2.2. 性能优化技巧:避免管道中的性能陷阱

PowerShell 的管道非常强大,但也可能成为性能瓶颈,尤其是在处理大量数据时。理解其工作原理并采取相应的优化措施,能让你的脚本运行效率产生质的飞跃。

  • 尽早过滤(Filter Left): 这是最重要的 PowerShell 性能优化原则。管道的工作方式是逐个传递对象。你应该在管道的最左端(尽可能早地)就过滤掉不需要的数据,而不是将所有数据都传递到管道的右端再进行筛选。

    反例(慢)

    powershell

    # 获取所有服务,传递给 Where-Object 进行筛选
    Get-Service | Where-Object { $_.Status -eq 'Running' }
    

    正例(快)

    powershell

    # 利用 Get-Service 自带的 -State 参数,在源头就完成筛选
    Get-Service -State Running
    

    许多 Cmdlet(如 Get-ChildItem, Get-ADUser, Get-CimInstance)都提供了 -Filter, -Include 等参数。利用这些参数进行“左过滤”,可以让数据在源头就被筛选掉,极大地减少了通过管道传输的对象数量,效率天差地别。

  • 避免在 ForEach-Object 中重复获取数据: 当在循环中需要重复使用某个不变的数据集时,应在循环外部预先将其加载到变量中。

    反例(极慢)

    powershell

    $userNames | ForEach-Object {
        # 每次循环都去获取一次所有 AD 组,非常低效
        $groups = Get-ADGroup -Filter *
        # ...
    }
    

    正例(快)

    powershell

    # 在循环开始前,一次性获取所有组
    $allGroups = Get-ADGroup -Filter *
    $userNames | ForEach-Object {
        # 直接使用预先加载的数据
        # ...
    }
    
  • 选择更快的运算符

    • 在处理字符串时,-like 运算符(使用通配符)通常比 -match 运算符(使用正则表达式)要快得多。如果简单的通配符就能满足需求,就不要使用正则表达式。
    • 在比较集合时,-contains 运算符(检查左侧集合是否包含右侧的单个项)通常比 -in 运算符(检查左侧单个项是否存在于右侧集合中)性能更好,尤其是在大集合中。
  • 使用 StringBuilder 来构建大字符串: 在循环中反复使用 += 来拼接字符串,会因为每次都创建新字符串而导致性能急剧下降。对于需要构建大字符串的场景,应使用 .NET 的 System.Text.StringBuilder 类。

    正例(高效)

    powershell

    $sb = [System.Text.StringBuilder]::new()
    1..10000 | ForEach-Object {
        [void]$sb.Append("Line $_`n")
    }
    $finalString = $sb.ToString()
    
9.2.3. 注释的艺术:编写自己和别人都能看懂的注释

好的代码应当是自解释的,但好的注释则能提供代码本身无法表达的上下文和意图。注释不是代码的复述,而是代码的“为什么”和“是什么”。

  • 注释什么?

    • “为什么”这么做:解释你做出某个特定设计决策的原因。为什么选择这个算法?为什么这里要特殊处理某个边界情况?

      powershell

      # 使用 -Filter 而不是 Where-Object,因为 AD 的 LDAP 筛选器在域控制器端执行,效率更高。
      Get-ADUser -Filter "Name -like '*$name*'"
      
    • 复杂的逻辑:为一段复杂的正则表达式、算法或业务逻辑提供一个简明的自然语言摘要。

      powershell

      # 这个正则表达式用于匹配一个有效的语义化版本号 (e.g., 1.2.3-beta.4)
      $semVerPattern = '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
      
    • 临时的解决方案或待办事项:使用 TODO: 或 FIXME: 等标准标签来标记需要后续关注的代码。

      powershell

      # TODO: 当前 API Key 是硬编码的,未来需要迁移到 Azure Key Vault。
      $apiKey = "secret-key"
      
  • 注释的类型

    • 单行注释:使用 #,用于简短的行内或行上注释。
    • 块注释:使用 <# ... #>,用于多行注释,或者临时注释掉一整块代码。
    • 注释式帮助(Comment-Based Help):我们在 5.1.3 节学过的,为函数编写专业的、可由 Get-Help 读取的帮助文档。这是任何一个要被重用的函数都应该拥有的“标配”。

注释的黄金法则:编写注释时,想象一下六个月后的你,或者一个完全不了解这个项目的同事,他们需要知道什么才能快速理解并维护这段代码。


通过遵循社区风格指南、掌握性能优化技巧和运用恰到好处的注释,我们正在将我们的代码,从一个能用的“工具”,升华为一件可信赖的“工艺品”。这样的代码,不仅运行得更出色,也为未来的维护和团队协作铺平了道路,这正是一位专业 PowerShell 工程师价值的真正体现。


9.3. 使用 Git 进行脚本版本控制

我们已经学会了如何构建坚不可摧的安全堡垒,也掌握了打造优雅高效代码的工匠技艺。现在,我们要为我们所有的心血结晶,请来一位最可靠的“历史学家”和“守护神”。

想象一下,你精心编写的一个复杂脚本,在一次修改后突然无法工作了,你迫切地想知道“我到底改了什么?”;或者,你和你的团队成员需要同时对同一个模块进行开发,你们如何确保各自的工作不会相互覆盖、造成混乱?再或者,一场突如其来的灾难(如硬盘损坏)让你丢失了所有的代码,你是否有能力瞬间将它们恢复到任何一个历史版本?

解决这一切问题的答案,就是版本控制(Version Control)。本节,我们将学习当今世界上最流行、最强大的版本控制系统——Git。掌握 Git,就如同为你的代码开启了“时光机”和“无限副本”的能力。它不仅是个人项目管理的利器,更是现代所有软件开发团队协作的基石。

在专业的软件开发领域,任何没有被版本控制系统管理的代码,都可以被认为是“不存在的”。对于 PowerShell 脚本和模块开发而言,这个原则同样适用。将你的代码纳入版本控制,是你从“脚本小子”向“软件工程师”转变的最后,也是最关键的一步。本节将带你入门 Git,这个强大、免费、开源的分布式版本控制系统。

9.3.1. 为何需要版本控制:追踪变更、协同工作、安全回滚

版本控制系统(VCS)为你和你的团队带来了三大核心价值:

  1. 完整的变更历史(时光机)

    • Git 会像一个一丝不苟的史官,记录下你对代码的每一次有意义的修改(称为一次“提交”或 “commit”)。
    • 每一次提交都包含了修改了哪些文件、具体修改了什么内容、是谁修改的、在什么时间修改的,以及为什么要修改(通过提交信息来描述)。
    • 你可以随时“穿越”回任何一个历史版本,查看当时的代码状态,或者比较任意两个版本之间的差异。
  2. 高效的协同工作(团队大脑)

    • Git 允许多个开发者在各自的计算机上,对同一个项目进行独立的、并行的开发,而不会相互干扰。
    • 它提供了一套强大的机制(如分支和合并),来帮助团队成员将各自完成的工作,安全、可控地整合到主代码库中。
    • 它充当了项目的“单一事实来源(Single Source of Truth)”,确保每个成员都工作在最新的、一致的代码基础上。
  3. 安全的分支与回滚(安全网)

    • 你可以随时创建一个代码的“实验副本”(称为“分支”或 “branch”),在上面进行新功能开发或 Bug 修复,而完全不影响主版本的稳定性。实验成功后,再将其合并回去。
    • 如果一次更新引入了严重的问题,你可以通过 Git 在几秒钟之内,将整个项目“回滚”到上一个稳定版本,极大地降低了变更带来的风险。
9.3.2. Git 核心概念与命令

要开始使用 Git,你需要先在你的系统上安装它(从 https://git-scm.com 下载),然后了解几个最核心的概念和命令。

  • 仓库(Repository, or Repo):这就是你的项目文件夹,Git 会在其中创建一个隐藏的 .git 子目录,用来存放所有的历史记录和元数据。
  • 工作区(Working Directory):你当前能看到和编辑的文件。
  • 暂存区(Staging Area, or Index):一个介于工作区和仓库之间的“待提交”区域。它允许你精确地选择工作区中的哪些变更,要被包含在下一次的提交中。

核心工作流与命令:

  1. git init:在一个新的或已存在的项目文件夹中,初始化一个新的 Git 仓库。

    bash

    cd C:\MyPowerShellModule
    git init
    
  2. git add <file>:将工作区中指定文件的变更,添加到暂存区。

    bash

    # 将单个文件的变更添加到暂存区
    git add .\MyModule.psm1
     
    # 使用 . 来添加当前目录下所有文件的变更
    git add .
    
  3. git commit -m "Your commit message":将暂存区中的所有变更,正式地“提交”到仓库中,形成一个新的历史版本。

    • 提交信息(commit message)至关重要!它应该简明扼要地描述“这次提交做了什么”。一个好的提交信息格式是:第一行是摘要(如 Feat: Add user creation function),空一行后是更详细的描述。

    bash

    git commit -m "Feat: Add initial function to create new users"
    
  4. git status:查看当前工作区和暂存区的状态。这是你最常用的命令,它会告诉你哪些文件被修改了、哪些文件在暂存区、哪些文件还没有被 Git 追踪。

  5. git log:查看项目的提交历史。

  6. git clone <url>:从一个远程服务器(如 GitHub)上,克隆一个已存在的仓库到你的本地计算机。

  7. git pull:从远程仓库拉取最新的变更,并与你的本地工作区合并。

  8. git push:将你的本地提交,推送到远程仓库,以便与团队成员分享。

9.3.3. 分支策略:安全地进行新功能开发和 Bug 修复

**分支(Branch)**是 Git 最强大的功能,也是其精髓所在。一个分支,就是一条独立的时间线。

  • main (或 master) 分支:通常,仓库的主分支被称为 mainmaster。它应该永远保持稳定、可随时部署的状态。绝对不要直接在主分支上进行开发。

  • 功能分支(Feature Branch):当你需要开发一个新功能时,你应该从 main 分支创建一个新的分支。

    bash

    # 从当前分支创建一个名为 "feature/add-logging" 的新分支,并切换过去
    git checkout -b feature/add-logging
    

    现在,你可以在这个 feature/add-logging 分支上自由地进行编码、提交。所有的修改都只发生在这条独立的时间线上,完全不会影响到 main 分支。

  • 合并(Merge):当新功能开发完成并通过测试后,你就可以将这个功能分支,合并回 main 分支。

    bash

    # 1. 首先,切换回主分支
    git checkout main
     
    # 2. 确保主分支是最新状态
    git pull
     
    # 3. 将功能分支合并进来
    git merge feature/add-logging
    

    Git 会智能地将功能分支上的所有变更,应用到 main 分支上。

这种“先开分支,再开发,后合并”的工作流,是所有专业团队的基本实践。它保证了主干的稳定,并使得并行开发成为可能。

9.3.4. 将你的项目托管在 GitHub/GitLab:参与开源与团队协作

虽然 Git 是分布式的,你完全可以在本地使用它。但要实现团队协作和代码备份,你需要一个远程仓库(Remote Repository)GitHubGitLab 是当今最流行的两个代码托管平台。

它们为你提供了:

  • 一个在云端的、可靠的 Git 仓库存储。
  • 漂亮的 Web 界面来浏览代码、查看历史。
  • 强大的团队协作功能,如拉取请求(Pull Request, or Merge Request)。这是一个正式的、用于代码审查的流程:当你的功能分支完成后,你创建一个 PR,邀请团队成员来审查你的代码,提出修改意见。只有在代码被批准后,才能被合并到主分支。
  • 问题追踪(Issue Tracking)、项目看板(Project Boards)、自动化CI/CD(持续集成/持续部署)等一整套软件开发生命周期管理工具。

将你的 PowerShell 模块托管在 GitHub 上,不仅能让你与团队高效协作,更是你向全世界展示你的技能、参与开源社区、为 PowerShell 生态做出贡献的最佳方式。


至此,我们已经为我们的 PowerShell 之旅,画上了一个圆满的句号。通过掌握 Git,我们不仅为我们的代码找到了最可靠的“守护神”,更获得了通往现代软件工程和团队协作世界的“门票”。你手中的 PowerShell,已经不再仅仅是一门脚本语言,而是一个完整的、专业的、能够构建和管理复杂系统的强大平台。

读者朋友们,请记住,这本书的结束,才是你真正精彩的 PowerShell 旅程的开始。带着这份知识、技能和专业的素养,去探索、去创造、去自动化你所能触及的一切吧!世界在你的脚下。