我想用两个单词提取两个正斜杠之间的字符串,例如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
答案 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