在bash中提取包含数字部分的路径

9
在bash中,假设给定了一个路径:
mypath='my/path/to/version/5e/is/7/here'

我想要提取包含数字的第一个部分。例如,我想要提取:5e

是否有比使用while循环遍历每个部分并检查其中是否有数字更好的方法?

while IFS=/ read part
do
   if [[ $part =~ *[0-9]* ]]; then
      echo "$part"
   fi
done <<< "$mypath"
7个回答

9

使用 Bash 的正则表达式:

[[ "$mypath" =~ [^/]*[0-9]+[^/]* ]] && echo "${BASH_REMATCH[0]}" 
5e

3

使用 'grep -o' 方法。

echo $mypath | grep -o -E '\b[^/]*[0-9][^/]*\b' | head -1

2
  1. /替换为换行符
  2. 筛选第一个匹配的数字
mypath='my/path/to/version/5e/is/7/here'
<<<"${mypath//\//$'\n'}" grep -m1 '[0-9]'

如果路径中包含换行符,则可以使用零分隔流和GNU工具作为更安全的替代方法:

<<<"${mypath}" tr '/' '\0' | grep -z -m1 '[0-9]'

有没有比使用while循环并检查每个部分是否为数字更好的方法?

没有,无论如何,您都必须遍历所有部分,直到发现第一个带数字的部分。循环可能隐藏在其他工具后面,但它仍将遍历各个部分。您的解决方案本身似乎非常不错,如果您只想要第一个部分,请在找到第一个部分后使用break


1

请尝试以下代码,已使用示例进行编写和测试。如果行中有多个值,则应打印此内容。如果要讨论更好的方法,awk 可能比纯 bash 循环 + 正则表达式解决方案更快,所以在此添加它。

awk -F'/' '
{
  val=""
  for(i=1;i<=NF;i++){
    if($i~/[0-9][a-zA-Z]/ || $i~/[a-zA-Z][0-9]/){
      val=(val?val OFS:"")$i
    }
  }
  print val
}' Input_file

说明:为上述内容添加详细解释。

awk -F'/' '                                              ##Starting awk program from here and setting field separator as / here.
{
  val=""                                                 ##Nullifying val here.
  for(i=1;i<=NF;i++){                                    ##Running for loop till value of NF.
    if($i~/[0-9][a-zA-Z]/ || $i~/[a-zA-Z][0-9]/){        ##Checking condition if field value is matching regex of digit alphabet then do following.
      val=(val?val OFS:"")$i                             ##Creating variable val where keep on adding current field value in it.
    }
  }
  print val                                              ##Printing val here.
}' Input_file                                            ##Mentioning Input_file name here.

1
使用Perl:
mypath='my/path/to/version/5e/is/7/here'

# Method 1 (using for loop):
echo "${mypath}" | perl -F'/' -lane 'for my $dir ( @F ) { next unless $dir =~ /\d/; print $dir; last; }'

# Method 2 (using grep):
echo "${mypath}" | perl -F'/' -lane 'my $dir = ( grep { /\d/ } @F )[0]; print $dir if defined $dir;'

# Prints:
# 5e

这个 Perl 一行命令使用了以下命令行标志:
-e :告诉 Perl 在行内查找代码,而不是在文件中。
-n :逐行循环输入,将其默认赋值给 $_
-l :在执行行内代码之前剥离输入行分隔符(默认为 *NIX 上的 "\n"),并在打印时附加它。
-a :在空格或 -F 选项指定的正则表达式上将 $_ 分割成数组 @F
-F'/' :在 / 上分割为 @F,而不是在空格上。

next unless $dir =~ /\d/;:如果路径的当前部分不包含数字(\d),则跳过循环的其余部分。
last;:退出循环(在这里也退出脚本),以便仅打印匹配目录的第一个实例。
grep { ... } LIST:对于LIST参数,返回表达式...为真的元素列表,在此处返回具有数字的所有路径元素列表。
(LIST)[0]:返回LIST的第一个元素,在此处为具有数字的第一个路径元素。

另请参阅:

perldoc perlrun: 如何执行Perl解释器:命令行开关

perldoc perlre: Perl正则表达式(regexes)

perldoc perlre: Perl正则表达式(regexes):量词;字符类和其他特殊转义符;断言;捕获组


1
使用 awk,将 RS 设置为 / 并打印第一个包含数字的记录。
awk -v RS=/ '/[0-9]/{print;exit}' <<< "$mypath"
5e

0

另一种Bash变体

mypath='my/path/to/app version/5e/is/7/here'
until [[ ${mypath:0:1} =~ [0-9] ]]; do
    mypath=${mypath#*/}
done
echo ${mypath%%/*}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接