【问题标题】:escape content of variable for cd为 cd 转义变量内容
【发布时间】:2022-11-28 08:25:51
【问题描述】:

我正在使用 Powershell,我循环遍历文件并创建一个包含每个文件名的文件夹以将一些数据放在那里。

$files = @("[som]video.mkv")
$tmp_location = "." 
# to reproduce just do it on files with a filename like [somen_id]restofname.ext 
foreach ($file in $files){
    $base_input = ([io.fileinfo]$file).basename
    # base input may be a file called: [somen_id]restofname.ext 
    $tmp_dir = "$tmp_location/$base_input"
    mkdir $tmp_dir  # this line works and the directory is created
    # do some stuff first before cd
    cd $tmp_dir #this does not work

}

cd 无法处理 tmp_dir 变量,当它有像 [] 这样的特殊字符时,但是 mkdir(甚至 rm)创建/删除那个目录就好了,这在 Powershell 中是一个非常不一致的行为,我希望它要么失败要么为所有人工作!

知道如何转义变量,使其对cd 可读吗

(在现实生活中我的数组不仅仅是手写的 1 个文件名,但是这个例子也显示了错误)

谢谢

【问题讨论】:

  • 我不会将 [] 放在路径名中。 Powershell 和.net 会遇到很多麻烦。
  • @js2010,问题是 PowerShell 特有的,由于它的考虑 [] 元字符的通配符语言。我认为底层 .NET API 不会受到影响。
  • @js2010 "I wouldn't put [] in path names",不是真正的选择......至少在我的特定用例中不是

标签: powershell


【解决方案1】:

tl;博士

使用-LiteralPath传递要解释的路径的参数字面上地(逐字)到 Set-Location cmdlet,cd 是内置的别名的;默认情况下(通过位置隐含的-Path参数),它被解释为wildcard expression

# * "cd" is a built-in alias of "Set-Location"
# * "sl" is the preferable, PowerShell-idiomatic built-in alias
# * Interactively, using PowerShell's elastic syntax, 
#   you can shorten "-LiteralPath" to "-l", given that no other parameter name
#   (currently) starts with "l"
# * In PowerShell (Core) 7+, "-lp" is an official alias.
cd -LiteralPath $tmp_dir 

相比之下,mkdir(这是New-Item -ItemType Directory 的包装函数)含蓄地对待它的论点字面上地.

继续阅读以了解详细信息。


至于你试过的:

尽管cdmkdir喜欢他们的cmd.exe同行, 他们不是:

  • cdSet-Location cmdlet 的内置别名。

  • mkdir 是内置的包装函数对于 New-Item cmdlet,隐式应用参数 -ItemType Directory

要了解给定名称(最终)在 PowerShell 中指的是什么命令,请使用 Get-Command cmdlet;例如Get-Command cd)

因此,

cd $tmp_dir

等同于以下调用,前提是Set-Location绑定一个定位的争论(一个不以目标参数名称开头)到它的-Path参数:

Set-Location -Path $tmp_dir

最多PowerShell cmdlet 将 -Path 参数解释为 wildcard expressions,包括 - 也许令人惊讶 - Set-Location[1]

通常-Path-LiteralPath 之间的区别并不重要,因为 *? - 通常的通配符 - 甚至都不是允许在文件和目录名称中(至少在 Windows 上)。

然而,问题是PowerShell的通配符语言[]有特殊意义(它们形成字符集——例如[abc]和/或字符范围——例如[a-c]),其中冲突文字在文件名中使用 [],并且需要使用 -LiteralPath 来消除歧义。[2]

相比之下,New-Item的(可能是隐含的位置)-Path参数表现得像-LiteralPath,因为在上下文中将路径参数解释为通配符表达式创造文件或目录毫无意义。这就是为什么您可以毫无问题地创建一个目录,其路径字面上包含 [] 以及 mkdir[3]


为什么使用以 a 命名的别名不同的最好避免在 PowerShell 中使用 shell 的命令,例如 cdmkdir

在为cmd.exe 用户(以及部分 POSIX 兼容 shell 的用户)减轻迁移痛苦的有问题的尝试中,PowerShell 决定定义以cmd.exe 的内部命令命名的内置别名和包装函数,而 PowerShell 的类似内部命令,即所谓的命令, 有非常不同的名字。

后者本身没有问题 - 除了通过使用它们你会错过 PowerShell 的标准动词 - 名词命名约定(例如,Set-Location)的好处,这也延伸到如何别名假设 approved verbs 具有官方别名形式(例如,s 代表 Set-;因此,sl 是另一个别名,但是PowerShell 惯用语Set-Location别名)。

什么然而,有问题的是PowerShell 命令有 very different syntax 来自 cmd.exe's.

如果您使用诸如 cdmkdir 之类的名称,您会很想这样想,例如cdmkdir 的功能与 cmd.exe 中的相同 - 然而,这仅适用于最基本的用例。

最好使用真正的 PowerShell 命令名称或 - 为了简洁起见交互的使用 - 他们的电源外壳别名,它们是(合理地)可预测地形成的,如上所述(例如,sl代表Set-Locationni代表New-Item


[1] 相比之下,cmd.execd命令确实不是接受通配符。对于Set-Location,通配符支持在概念上是必要的,因为通配符表达式必须解析为恰好一个匹配目录。

[2] 或者,使用-Path你可以逃脱[] 分别为 `[`],但这种转义在 PowerShell 7.3.0 中并不一致 - 请参阅 GitHub issue #7999

[3] 不幸的是,这种参数命名不一致;可以说,-LiteralPath 至少应该作为参数得到支持别名对于-Path。也就是说,从 PowerShell 7.3.0 开始,实际上有一种情况是 -Path 当前解释为通配符表达式,即当结合 -Name 参数, 但这应该被认为是漏洞- 见GitHub issue #17106

【讨论】:

  • 谢谢,它起作用了,知道为什么 cd 默认为这种行为,但 mkdir 和 rm 的行为就好像 -l 已经设置了吗?也许是一些历史原因?
  • @user206904,我稍后会在答案中添加一个更新,但它的简称是:mkdir,它是New-Item的包装函数,根本不支持通配符(对于创建文件/目录。那没有多大意义)。 rm,这是Remove-Item的别名区分 -Path-LiteralPath,但是,就像 mkdir / New-Item 一样。
  • @user206904,请看我的更新。
猜你喜欢
  • 2020-06-24
  • 2015-08-13
  • 2020-08-11
  • 2019-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
相关资源
最近更新 更多