使用sed,grep中断来提取特殊字符的斜线之间的匹配组

时间:2018-09-08 02:54:27

标签: regex perl awk sed grep

我想用两个单词提取两个正斜杠之间的字符串,例如test作为字符串的开始(在第一个斜杠之前),但在最后一个斜杠之后及其内容之后是可选的。我能够正确提取它,但是如果要提取的字符串中有特殊字符,则应该失败。

some_word/part_i_want_to_extract/optional_string
                                ^               
                                | from here is optional (including /)

我要提取的部分也应该只包含字母数字_-

sed的示例:

echo 'test/alpha_numeric-9034/something' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numeric-9034

echo 'test/alpha_numer$ic-9034/something' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numer

echo 'test/alpha_numer$ic-9034' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numer

我想让第二/三分之一失败(应该什么也不回报),我该怎么做?

grep中的相同示例:

echo 'test/alpha_numeric-9034/something' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numeric-9034

echo 'test/alpha_numer$ic-9034/something' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numer

echo 'test/alpha_numer$ic-9034' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numer

在grep中,最后使用$也没有任何作用。 Here is a demo适用于基于php的正则表达式,我找不到任何有效的perl工具。任何帮助将不胜感激。

有关应返回什么的更多示例

'test/alpha_numeric-9034/something' -> alpha_numeric-9034
'test/alpha_numer$ic-9034/something' -> should be nothing (since it has $)
'test/alpha_numeric-9034' -> alpha_numeric-9034
'test/QR-9034' -> QR-9034

4 个答案:

答案 0 :(得分:2)

更新。为上一个问题编辑添加了一个版本。


使用Perl(带有标签),使用文件data.txt中的回显数据行

perl -wnE'@m = m{^test/([\w-]+)(?=/)}g; say "@m" if @m' data.txt

这仅从第一行打印alpha_numeric-9034

我使用[\w-],如果需要,可以通过POSIX character class[[:alnum:]_-]进行更改。另一个选择是在否定的类中列出禁止的字符以及/[^/...]

该代码捕获到^test/之后到下一个/为止的允许字符,并使用积极的前瞻性断言/在那儿。作为“ 零宽度断言”的前瞻,不会消耗该斜杠,否则它将以两个以上的斜杠失败。

假定路径为目标,代码仅在有斜线之间捕获斜线之间,丢弃//但仍与该行匹配。如果您希望斜杠之间不包含任何内容,请将+量词更改为*,那么您将获得一对//的空字符串。

它也可以与任意数量的斜杠一起使用,提取连续斜杠之间的内容。通过在上面使用的文件中添加一行test/first/yet/more/end进行测试,

alpha_numeric-9034
first yet more

注意 ,最后一个问题的编辑允许使用test/QR-9034,因此是第二个斜杠。这与原始声明和明确的早期澄清相矛盾,上面(Perl)和下面(bash)的解决方案不是为此设计的,在这种情况下不起作用。

但是,最多只能有两个斜杠,这样(很)容易

perl -wnE'say $1 if m{^test/([\w-]+)/?}' data.txt

此匹配上面^test/之后的内容,直到下一个可选?)斜杠。


如果这确实与解析路径有关,请使用精确地执行此操作的模块。


我们明确了目标是使用“最小工具/语言支持” 以bash的方式进行。然后可以直接在bash中完成。这不会像Perl那样简洁,但是没有使用其他工具。一种方式

#!/bin/bash

string='test/one/two/end'

# Build array of fields using / for the separator
IFS='/' read -ra ary <<< "$string"

# Note: don't know how the presumed bash script is organized
# Use checks below (or alternatives) for flow control you need

# Check for non :alnum: characters. Iterating is a bit slow but clear
for i in "${ary[@]}"; do
    if [[ "$i" =~ [^[:alnum:]] ]]; then
        echo "Element $i has non-alnum"
        has_special=1
        break
    fi  
done

if [[ ${#ary[@]} -le 1 || ${ary[0]} != "test" || $has_special ]]; then
    echo "No match"
else
    # Remove first and last elements
    unset 'ary[${#ary[@]}-1]'
    unset 'ary[${ary[0]}]'

    echo "${ary[@]}"
 fi

这将打印以下行:one two(或No match,如果字符串/ $IFS更改为失败)

以上所有部分都可以通过其他方式完成。评论

  • 简单地给出检查(针对非数字,test/和整体匹配),因为未指定哪种流量控制合适。为更好的程序组织进行重组

  • 用于检查数组元素的迭代是清晰但缓慢的;还有其他方法。如果对此有兴趣,请告诉我,我将进行编辑。另外,可以检查字符串本身,但是我们不能(简单地)使用:alnum:,因为它确实包含/

  • read实际上是通过分隔符将字符串分隔成数组的最有效方法,而没有派生或外部工具或资源

  • 在更新版本的bash(4.3+?)上,您可以进行unset 'array[-1]'

  • 在最近的bash中,上述更改$IFS仅在当前命令内

  • 如果没有匹配项,则整个字符串位于ary的第一个元素中,因此我检查大小

答案 1 :(得分:2)

保持清晰,简单,高效,健壮,可移植等优点,只需使用awk:

collection.count()

或:

$ awk -F'/' '($1=="test") && ($2~/^[[:alnum:]_-]+$/){print $2}' file
alpha_numeric-9034
alpha_numeric-9034
QR-9034

取决于是否不希望输入或在给定不匹配输入的情况下输出空白行。

答案 2 :(得分:1)

这里是awk

awk -F\/ 'NF>2 && $2 ~ /^[0-9a-zA-Z_-]+$/ {print $2}'
alpha_numeric-9034

或者这个:

awk -F\/ 'NF>2 && $2 ~ /^[[:alnum:]_-]+$/ {print $2}'
alpha_numeric-9034

或者这个:

awk -F\/ 'NF>2 && $2 !~ /[!@#$%^&*()+=~]/ {print $2}'
alpha_numeric-9034

测试是否包含最少2个/,并在其中包含正确值的情况下打印//之间的第一个数据
PS我会说-_是特殊字符

答案 3 :(得分:1)

我要添加一个sed解决方案,但是sed并不是一个理想的选择:

sed -r 's~^test/([[:alnum:]_-]*)(/|$)|.~\1~g'

这将查找从test开始,遵循/[[:alnum:]_-]*模式直到/或输入字符串结尾的输入字符串。轮换的另一端是一个应该与失败相匹配的时期。 g标志也已启用。 (我不确定sed -r 's~^test/([[:alnum:]_-]*)(/|$)|.*~\1~'为何不起作用。如果有人暗示他/她是受欢迎的。)

测试用例:

$ echo 'test/al_num-0$' | ...

$ echo 'test/al_num-0' | ...
al_num-0
$ echo 'test/al_num-0/something' | ...
al_num-0