【问题标题】:Convert bash script using regexes to sh使用正则表达式将 bash 脚本转换为 sh
【发布时间】:2015-02-09 06:35:31
【问题描述】:

我真的很难写一个 Bourne shell 脚本。基本上,我试图检测一个名为“ref”的变量的三种输入格式:

  1. ref="refs/head/.*"(即以“refs/head/”开头,我对斜线后面的位感兴趣)
  2. ref="refs/tags/.*"(即以“refs/tags/”开头,我对斜线后面的位感兴趣)
  3. 其他所有内容(即忽略不以“refs/head/”或“refs/tags/”开头的所有内容)

例如,

如果 ref="refs/head/master",设置 TAG="master"

如果 ref="refs/tags/0.2.4",设置 TAG="0.2.4"

对于其他所有内容,设置 TAG=""

现在我在 bash shell 中写了一些东西,但我真的很难将它转换为 Bourne (#!/bin/sh):

#!/bin/bash
#
#This works!
#
TAG=""
re='^refs/head/.*'   #regex: begins with refs/head/, ends with anything
re2='^refs/tags/.*'   #regex: begins with refs/tags/, ends with anything
if [[ $ref =~ $re ]]; then
        #do nothing - OK
        true   #NOP
else    
        #check if it's a tag update
        if [[ $ref =~ $re2 ]]; then
                TAG=${$ref##*/}   #looks worse that it is: http://stackoverflow.com/questions/3162385/how-to-split-a-string-in-shell-and-get-the-last-field
        fi
        exit 0
fi

echo $TAG

我花了很长时间才 a) 编写这个程序 b) 找出为什么我的程序会发疯 - 结果我需要 #!/bin/sh 而不是 #!/bin/bash

如何将其转换为 sh?也许其他人对我的正则表达式体操有更优雅的解决方案?


更新:

感谢到目前为止的回答(尤其是@gboffi)。我想我快到了。

我现在只需要知道 $TAG 是来自“refs/head/”、“refs/tags/”还是两者都没有。我试图修改一些答案,但真的很挣扎 sh。我需要离开并从第一原理中了解更多关于 sh 的信息,而不是试图破解它。

更新 2:

所以睡了一夜之后,我在大约 20 分钟内搞定了。这是我的解决方案:

#!/bin/sh

ref="refs/asdf/master"

TAG=""
TAG="${ref#refs/heads/}"
if [ "$ref" != "${ref#refs/heads/}" ]; then
        echo "heads"
        echo $TAG
else
        TAG="${ref#refs/tags/}"
        if [ "$ref" != "${ref#refs/tags/}" ]; then
                echo "heads"
                echo $TAG
        else
                TAG=""
        fi
fi

echo "--->$TAG"

我确信有一个更优雅的解决方案;但我就是没时间!

【问题讨论】:

  • 出于好奇,为什么需要将其从 bash 转换为 sh? sh 没有内置的正则表达式支持,所以它基本上需要重写。
  • @JohnKugelman - 此外,为什么不使用 grep、sed 和 awk 等核心 Unix 工具?真的需要纯shell吗?
  • @JohnKugelman 恐怕我在使用grepsedawk 时遇到问题。您能否建议我如何在 sh 脚本中使用这些标准工具来获取“/”上拆分的最后一个元素?
  • 如果您有一个功能齐全的解决方案,请将其从问题中取出并作为答案,然后将答案视为正确答案。

标签: linux bash shell unix sh


【解决方案1】:

这里是一个函数,定义在dashsh的linux版本)

% dash
$ tag() {
>  TAG=""
>  [ "$1" != "${1#refs/head/}" ] && TAG="${1#refs/head/}"
>  [ "$1" != "${1#refs/tags/}" ] && TAG="${1#refs/tags/}"
>  echo "$TAG"
>}
$ tag poldo

$ tag refs/head/poldo
poldo
$ tag refs/tags/pippo
pippo
$ tag to093u0refs/head/poldo

$exit
% 

【讨论】:

  • 嗨,看起来很不错。谢谢你。我一直在尝试稍微修改 - 我需要知道“poldo”/“pippo”是否来自 /refs/head/ 或 /refs/tags/...
  • 我不得不说,您最初的问题没有提到识别标签的问题,而且您向我们展示的代码中也没有反映出这一问题。也就是说,我认为有几种可能的、不同的解决方案,其中只有一个满足您的需求,您能否编辑您的问题来介绍这个新问题,详细解释您需要什么?如果你这么好心,我或其他人可能会给出与最重要的问题相关的答案,而不是一长串的 cmets。
【解决方案2】:

阅读了您对所需功能的描述后,似乎可以归结为:

  1. 检查输入参数。如果它以 refs/head 或 refs/master 开头,则继续,否则停止。
  2. 获取终端信息并将变量 TAG 设置为它。

因此,假设您的输入在变量 REF 中,

TAG=""
echo $REF | egrep -q 'refs/head|refs/master'
if [ $? -eq 0 ]
then
  TAG=`echo $REF | sed "s/.*\///"`
fi

应该这样做。

(shell 头的旁注:在这种特殊情况下,basename 是不是比 sed 更好的解决方案?)

【讨论】:

    【解决方案3】:

    处理这种情况最惯用的方法是使用 case 语句和模式匹配:

    #!/bin/sh
    case $ref in
      refs/head/*) true ;;                       # original behavior: no output, success
      refs/tags/*) printf '%s\n' "${ref##*/}" ;; # original behavior: emit output
      *) false # exit with an error              # original code didn't specify behavior
    esac
    

    refs/head/*refs/tags/* 都不匹配的情况下,我退出错误的原因是,如果您想在这种情况下成功退出,您可以完全省略refs/head/* 测试。

    【讨论】:

      【解决方案4】:

      您可以为此使用rev-parse,以下是我的一个示例中的一些示例 存储库。请注意,最后一个不会产生任何输出。

      $ git rev-parse --abbrev-ref refs/heads/master 掌握 $ git rev-parse --abbrev-ref refs/tags/5 5 $ git rev-parse --abbrev-ref @^

      【讨论】:

      • 优雅。非常适合这种情况。
      猜你喜欢
      • 1970-01-01
      • 2011-11-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 2013-06-06
      相关资源
      最近更新 更多