PowerShell序列化对象机制疑问及Set-Mailbox报错解析
咱们先从你碰到的错误入手,再深入讲序列化的事儿——毕竟你核心需求是搞懂机制,而不只是改UPN。
1. 为啥$_.name会返回空?
你原命令里的Set-Mailbox -UserPrincipalName $_.name+'@newco.com'犯了一个典型的PowerShell作用域错误:$_在这里根本不指向管道传过来的邮箱对象。
Where-Object确实会把符合条件的邮箱传到下一个管道节点,但Set-Mailbox的参数值里直接写$_,PowerShell没法识别这是当前管道的对象——因为Set-Mailbox虽然支持管道输入,但这种参数绑定方式不对。你得用ForEach-Object来显式遍历每个对象,这样$_才会正确指向当前循环的那个邮箱。
这才是你得到“属性不能为空”错误的直接原因:$_.name是空值,拼接后变成@newco.com,这显然不是合法的UPN,所以Set-Mailbox直接报错。
2. 聊聊Deserialized.对象到底是什么
你看到的Deserialized.Microsoft.Exchange.Data.Directory.Management.Mailbox,本质是远程PowerShell会话的产物:
Exchange Management Shell(EMS)其实是一个本地客户端,它通过远程PowerShell连接到Exchange服务器。当你执行Get-Mailbox时,命令实际在Exchange服务器上运行,服务器把结果对象转换成XML格式(这个过程就是序列化),然后把XML传到你的本地会话;本地再把XML还原成对象(反序列化),这就是你看到的Deserialized.开头的对象。
这种序列化后的对象有几个关键特点:
- 带
Deserialized.前缀,明确告诉你这不是内存里的原始对象,是还原出来的副本 - 只保留原始对象的公共属性值,没有原始对象的方法(比如你不能直接调用
.Update()之类的方法来修改邮箱) - 部分复杂属性可能会被简化甚至丢失(不过在这个场景里,
Name属性是完整保留的,所以不是它导致的空值)
3. 修正命令的正确姿势
要让你的逻辑生效,用ForEach-Object显式处理每个对象就行:
Get-Mailbox | Where-Object {$_.UserPrincipalName -like "*@network.local"} | ForEach-Object { Set-Mailbox -Identity $_.Identity -UserPrincipalName "$($_.Name)@newco.com" }
这里有两个细节要注意:
- 用
$($_.Name)做字符串插值,比直接用+拼接更可靠,避免出现意外的格式问题 - 显式加上
-Identity参数指定要修改的邮箱,虽然Set-Mailbox能通过管道接收对象,但显式指定会让逻辑更清晰,也不容易出错
4. 再深入理解PowerShell序列化的本质
序列化是PowerShell用来在不同会话/进程/设备之间传递复杂对象的机制,比如:
- 远程PowerShell会话(像EMS这种)
- 后台作业(
Start-Job) - 导出对象到文件(
Export-Clixml)再导入(Import-Clixml)
它的核心逻辑是:
- 序列化:把对象的类型信息、公共属性值转换成可存储/传输的格式(通常是XML)
- 反序列化:把XML还原成一个新对象,标记为
Deserialized.类型,填充保存的属性值
要记住的关键点:反序列化出来的对象只是原始对象的“快照副本”,修改这个副本不会影响实际数据源——比如你改了Deserialized.Mailbox的UserPrincipalName属性,Exchange服务器上的邮箱根本不会变,必须用Set-Mailbox这类命令去修改实际的数据。
内容的提问来源于stack exchange,提问作者Murad

