【问题标题】:Extracting part of path containing a number in bash在bash中提取包含数字的部分路径
【发布时间】:2021-01-04 08:33:27
【问题描述】:

在 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"

【问题讨论】:

    标签: regex bash awk


    【解决方案1】:

    使用 Bash 的正则表达式:

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

    【讨论】:

      【解决方案2】:

      使用 'grep -o' 的方法。

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

      【讨论】:

        【解决方案3】:
        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 在您找到第一部分之后。

        【讨论】:

          【解决方案4】:

          您能否尝试使用所示示例进行以下、编写和测试。如果我们在行中也有超过 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.
          

          【讨论】:

            【解决方案5】:

            使用 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: how to execute the Perl interpreter: command line switches
            perldoc perlre: Perl regular expressions (regexes)
            perldoc perlre: Perl regular expressions (regexes): Quantifiers; Character Classes and other Special Escapes; Assertions; Capture groups

            【讨论】:

              【解决方案6】:

              使用awk,将RS 设置为/ 并打印包含数字的第一条记录。

              awk -v RS=/ '/[0-9]/{print;exit}' <<< "$mypath"
              5e
              

              【讨论】:

                【解决方案7】:

                另一个 bash 变体

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-03-02
                  • 1970-01-01
                  • 2021-10-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多