在最后一条命令中用反斜杠替换正斜杠

在最后一条命令中用反斜杠替换正斜杠

我已将命令cd C:\foo\bar\从 PowerShell 复制到 Cygwin,并愚蠢地希望它能够执行。我现在正尝试运行替换,将所有替换\/

$ !!:gs/\\/\/
bash: :gs/\\/\/: substitution failed

不确定为什么我会收到替换失败的消息。

我也尝试过:

$ !!:gs/\\/q

只是想看看替换是否是问题所在。不是。现在我很好奇!

为什么我会收到“替换失败”的消息?

答案1

正斜杠也是替换命令的分隔符,因此必须将其转义为替换字符串,以免提前结束替换

!!:gs/\\/\//

或者更明确地如评论中所建议的那样,使用斜线以外的其他符号作为分隔符

!!:gs|\\|/|

但这似乎只在(我所在位置通常分配的 shell)中起作用tcsh,我无法让命令起作用,bash因为似乎转义对第一个参数不起作用:s

答案2

简短回答:内置fc(修复命令)的 bash

fc是命令,内置在 bash shell 中,用于编辑和重新执行历史命令。
它是也出现在 CygWin 上并且它可以在我测试过的所有 Linux 发行版上运行:

fc  -s '\'='/' -1

一些解释

  • fc是 bash内置命令列出或编辑并重新执行历史列表中的命令。
  • -1将采用历史记录中的最后一条命令。请注意,even 的!!定义(从 读取man bash)为

    !! Refer to the previous command. This is a synonym for!-1“”。

  • -s用另一个模式替换一个模式。这次来自help fc(内置命令 so help):

    使用 `fc -s [pat=rep ...] [command]' 格式,在执行替换 OLD=NEW 之后,将重新执行 COMMAND。

    好的,他们的意思pat=rep是而不是OLD=NEW......

  • 这些模式将被 shell 读取,因此我们必须使用 quote:'\'和 来保护它们'/'

关于为什么会出现“替换失败”的更多内容

似乎对于s 修饰符尚未实现反斜杠字符的替换\,即转义字符。为确保万无一失,我们应该查看 bash 历史扩展的 gnu 版本代码(但有上述命令可以获取您尝试执行的操作……所以我偷懒....)。

一些说明:

  1. 我们被引导去认为它将适用于我们发现的每个与 一起使用的 RegEx sed,但这并不能保证。反斜杠是扩展的转义字符,问题就在这里。此外,扩展的行为与选项有关shopt,所以我们应该开始逐个查看...

  2. 当您将字符串粘贴cd C:\Foo\Bar到 bash shell 中时,它将被展开,并在解释器中显示为cd C:FooBar;以这种形式,它$_也将存储在内部变量中。
    如果您将cd "C:\Foo\Bar"或粘贴到变量cd 'C:\Foo\Bar'中,$_ 您应该会发现C:\Foo\Bar
    由于历史记录扩展是在读取完整行之后立即执行的,在 shell 将其分解为单词之前,您可能会想开始将它与一些巴什主义或多或少简单,例如,从中得出一些推导(可能添加:p:q""解析等等......)

    !!:0 ${_//\\/\/}
    

    此刻我们要记住,现在并不安全开始玩路径和文件名,特别是如果它们来自 Windows 剪贴板(一般阅读页面为什么不是解析ls,它本质上与使用制表符、空格和换行符作为文件名和目录名的正确字符的可能性有关……)。
    此外,当你粘贴用鼠标捕获的文本,您也可以粘贴前导空格。这可以避免您的命令在历史记录中结束(这取决于 shell 选项...)。如果是这样,您的后续!!命令将不受控制...(请参阅中的示例另一个答案)。这是一个不必要的有形风险

结论

历史扩展将历史列表中的单词引入输入流,让一切变得简单重复命令,将前一个命令的参数插入到当前输入行,或者快速修复前一个命令中的错误。

如果这不容易,我就会开始认为我们做错了什么;-)


令人厌烦:一个小实验

然后我已经histverify在 shell 中启用了......

shopt -s histverify
echo C:\Foo\Bar
!!:s|C|D| {1,2}A

然后我Enter按下已验证扩展我发现

echo D:\Foo\Bar {1,2}A

然后我Enter再次按下,它发出回声

D:FooBar 1A 2A

这似乎表明是substitution failed在之前处理的历史扩展中生成的括号扩展, 所以首先,并且它似乎证实s历史修改器没有(尚未)将字符替换处理\为真正的正则表达式......

答案3

您可以使用sed来替换并执行结果。

这样的命令有几种可能的变体,但在我的 bash 中只有这一个有效:

A=$(echo "!!\\" | sed 's,\\,/,g');eval $A

如果最后一条命令以反斜杠结尾,则\\在 is 后面添加 is 。如果没有它,shell 会感到困惑,并要求继续执行该行。!!

您还可以将其作为 bash 函数添加到文件中~/.bashrc

function slash {
   A=$(history | tail -n 2 | head -n 1 | cut -c 8- | sed 's,\\,/,g')
   eval $A
}

以下是我在 Windows 上的 Bash 中的工作方式:

狂欢

解释A=命令如何为 shell 变量分配正确的值A

  • tailcd \mnt\c从命令历史记录中获取最后两行,这将得出及其后的行slash。 的部分history | tail -n 2也可以写成history 2
  • head以第一行为例cd \mnt\c
  • cut删除由提供的行号history
  • sed用斜线替换所有反斜线
  • eval执行变量的内容A

答案4

我认为你不能这样做。你需要再次复制/粘贴。

这个问题与斜线作为替换命令分隔符无关,因为替换命令可以接受任何分隔符。问题在于要替换的反斜线,它们已经被视为右侧字符的简单无用转义符。

因此,当您调用替代命令时,反斜杠已从源中消失。只需尝试按原样重新调用命令(!!)。它不会打印包括 的完全相同的命令c:\foo,而是执行命令减去已处理的反斜杠,结果为c:foo

显然,您无法找到或替换缺失的字符。尝试这样做将引发错误。

相关内容