Shell awk,如何转义问号符号

Shell awk, how to escape question mark symbol

我正在尝试使用 shell 从 url 字符串中提取字符(使用 between 方法)。我坚持识别“?”性格...

set sample to "https://someaddress.com/path/subpath/12345?userId=523"
set extract to do shell script "awk -F 'subpath/|userId' '{print }'<<<" & quoted form of sample

...这会起作用,但显然会返回“12345?”。我如何也排除“?”。 “\?”没做

如果您不想要 ? 字符 ,则使用 \\? 代替 userId 作为 -F 选项的值,例如使用 -F 'subpath/|\\? 而不是 -F 'subpath/|userId':

set sample to "https://someaddress.com/path/subpath/12345?userId=523"
set extract to do shell script "awk -F 'subpath/|\\?' '{print }'<<<" & quoted form of sample
    --> "12345"

通常要从 awk 中的 命令行 转义 特殊字符 你必须使用两个 反斜杠,如下图示例 终端 输出所示:

$ awk -F 'subpath/|\?' '{print }'<<<'https://someaddress.com/path/subpath/12345?userId=523'
awk: illegal primary in regular expression subpath/|? at 
 input record number 1, file 
 source line number 1
$ awk -F 'subpath/|\?' '{print }'<<<'https://someaddress.com/path/subpath/12345?userId=523'
12345
$

但是,在 ApplesScript do shell script 命令 中,您必须加倍 反斜杠.

我猜(希望)您的代码片段是更大的 AppleScript 的一部分,否则我的直接建议是将整个内容编写为 shell 脚本,这实际上已经是。

我相信为正确的工作选择正确的工具,在这种情况下,shell 脚本和 AppleScripting 都可以单独完成,所以从一个人到另一个人的呼唤是非常懒惰和令人厌烦的。但是,个人品味在很大程度上决定了我们每个人喜欢编写脚本的方式,所以我会留给您来决定您是愿意采用我的任何一种方法,还是坚持使用现有的方法,因为它现在可以工作.

由于您的代码主要是 bash 脚本,我将从这里开始:awk 当然,它本身就是一种非常强大的脚本语言, 它可以用文本做很多很棒的事情。但它不是适合这里工作的工具:感觉就像拿起武士刀切面包;毫无疑问,它有能力这样做,但我不认为它是以最优雅的方式使用的,所以你最终弄得一团糟。我主要指的是正则表达式,这些正则表达式仅在应用于您提供的特定 URL 时才有用,并使用单词部分作为匹配目标,这是字符串的最后一个方面想被依靠。以下是我的做法:

$ awk -F '^.*/|[?=&]' '{ print ,,; }' <<< https://someaddress.com/path/subpath/12345?userId=523
12345 userId 523

使用 '^.*/|[?=&]' 进行模式匹配有两个优点:

  1. 应该立即注意到,这不会使用任何特定于您的 URL 的元素,因此,将适用于范围广泛的 URL通常遵循某种格式(我们可以相当有信心,例如,紧接在 slug 12345 之前的正斜杠将是出现在 URL 中的最后一个格式正确且正确 - encoded URL,因为它后面的任何其他内容都需要进行百分比编码)。

  2. 希望您对明显缺乏反斜杠感到满意,这种反斜杠一直在尝试双重转义,这是用一种语言编写脚本然后调用另一种语言的一个缺点,并且两者都需要专门为它们转义的字符串,即使它已经转义过一次。事实上,我设法将反斜杠的总数减少到零,因为我的正则表达式不包含任何需要转义的内容。这是嵌入在 AppleScript 中的相同脚本,让您看一看 运行 以确保它按原样工作:

    set www to "https://someaddress.com/path/subpath/12345?userId=523"
    set cmd to "awk -F '^.*/|[?=]' '{ print ,,; }' <<<"
    do shell script cmd & www's quoted form
        --> "12345 userId 523"
    
  3. 最后一个对某些人有价值但对其他人没有价值的好处是我在与您的基准测试时使用的正则表达式提高了效率和速度。人们并不总是认为正则表达式匹配是一组复杂、密集且执行成本合理的操作,并且在构建它们的方式上考虑周到——当然,在比这更严格的情况下---对脚本的性能有很大的影响。使用模式 subpath/|\? 需要执行 107 次文本比较以匹配 slug ("subpath") 和问号,耗时 444 毫秒。好吧,我很确定你可以为 运行 的脚本腾出 444 毫秒,当然,所以我并不是说它需要更快。但是,无论如何,使用 ^.*/|[?=&] 将操作数减少到 57 次,耗时 216 毫秒。因此,我们模式中相对无害的更改意味着脚本完成了一半的工作,因此速度提高了一倍。为了进一步说明对正则表达式的微小更改对性能的惊人影响,当我从模式前面删除插入符 (^) 时(这不会影响它匹配的内容,并且 returns 相同的最终结果),性能下降了 400%,需要 209 次文本比较才能在 901 毫秒内进行匹配。这几乎是整整一秒,所以它正在侵占您开始注意到的时间长度。


在讨论了 bash 方法之后,我将快速浏览 AppleScript-only 方法,这会让人觉得我将其优先级降低了,因为它不太合适,但事实上,我想说的是,在 这种 情况下,它可能是完成这项工作的更好工具。 awk 是武士刀,理想情况下我们更喜欢面包刀,但我们没有,但 AppleScript 是牛排刀,通常有点蹩脚,远没有那么锋利,但实际上最终会做一个非常整洁的工作,不会造成混乱。

set www to "https://someaddress.com/path/subpath/12345?userId=523"
set my text item delimiters to {"/", "?"}
return the text items of www
--> {"https:", "", "someaddress.com", "path", "subpath", "12345", "userId=523"}

和以前一样,它不关心具体的 URL 是什么,因为它会对任何格式正确的 URL 做同样的事情。由于 slug 位于 URL 中出现的最后一个正斜杠之前,并且在应该出现在其中的唯一问号后面,因此它总是会发现自己位于组件列表中的倒数第二个这个 AppleScript 非常令人愉快地分解了 URL。因此,您想要的 URL 的特定部分将从该脚本返回:

set www to "https://someaddress.com/path/subpath/12345?userId=523"
set my text item delimiters to {"/", "?"}
set slug to text item -2 of www
--> "12345"