Как увеличить номер версии в скрипте оболочки? - PullRequest
26 голосов
/ 28 декабря 2011

Следующий простой скрипт управления версиями предназначен для нахождения последнего номера версии данного файла, его увеличения, выполнения данной команды с вновь созданным файлом (например, редактором) и после этого сохранения его в стабильном. Поскольку это просто, он ничего не проверяет, так как скрипт будет изменен по мере необходимости. Например, если результат не будет стабильным, пользователь может пропустить последний аргумент.

Однако одна из основных проблем текущей функциональности заключается в том, как реализовать следующее: если последний раздел после точки имеет две цифры, в том числе до 99; если только 1, то inc до 9, затем переходите к предыдущему разделу. Версии могут иметь любое положительное целое число разделов.

1.2.3.44 -> 1.2.3.45
1.2.3.9 -> 1.2.4.0
1.2.3 -> 1.2.4
9 -> 10

Остается проблема в том, что он не ждет, пока редактор вина с вкладками закроет файл; цель состоит в том, чтобы определить, когда вкладка закрыта. Кроме того, не могли бы вы объяснить, как лучше всего убедиться, что имена моих переменных не перезаписывают существующие?

Вы также можете предложить другие улучшения.

#!/bin/bash
#Tested on bash 4.1.5
#All arguments in order: "folder with file" "file pattern" cmd [stable name]
folder="$1"
file_pattern="$2"
cmd="$3"
stable="$4"

cd "$folder"
last_version=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' | \
    sort -Vu | \
    tail -n 1)
last_version_file=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    grep $last_version | \
    tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number
new_version=$(echo $last_version | \
    gawk -F"." '{$NF+=1}{print $0RT}' OFS="." ORS="") #increments last section indefinitely
new_version_file=$(echo "$last_version_file" | \
    sed -r "s/$last_version/$new_version/")
cp "$last_version_file" "$new_version_file"
"$cmd" "$new_version_file" & \
    wait #works with gedit but not with wine tabbed editor
[[ "$stable" ]] && \
    cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Обновление: На моем компьютере работает следующее, я обновлю его, если будут найдены улучшения или решения нерешенных проблем:

#!/bin/bash
inc()
{
shopt -s extglob
    num=${last_version//./}
    let num++

    re=${last_version//./)(}
    re=${re//[0-9]/.}')'
    re=${re#*)}

    count=${last_version//[0-9]/}
    count=$(wc -c<<<$count)
    out=''
    for ((i=count-1;i>0;i--)) ; do
        out='.\\'$i$out
    done

    sed -r s/$re$/$out/ <<<$num
}

folder="$1"
file_pattern="$2"
cmd="$3"
stable="$4"

cd "$folder"
last_version=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' | \
    sort -Vu | \
    tail -n 1) #--almost-all do not list implied . and ..
last_version_file=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    grep $last_version | \
    tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number
new_version=$(inc)
new_version_file=$(echo "$last_version_file" | \
    sed -r "s/$last_version/$new_version/")
cp "$last_version_file" "$new_version_file"
"$cmd" "$new_version_file" && \
    wait #works with gedit but not tabbed wine editor
[[ "$stable" ]] && \
    cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Я ценю разнообразие предлагаемых решений, поскольку они помогают получить представление о перспективе и провести сравнение.

Ответы [ 8 ]

45 голосов
/ 28 декабря 2011
$ echo 1.2.3.4 | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}'
1.2.3.5

1.2.3.9  => 1.2.4.0
1.2.3.44 => 1.2.3.45
1.2.3.99 => 1.2.4.00
1.2.3.999=> 1.2.4.000
1.2.9    => 1.3.0
999      => 1000

UPDATE:

#!/usr/bin/gawk -f

BEGIN{
    v[1] = "1.2.3.4"
    v[2] = "1.2.3.44"
    v[3] = "1.2.3.99"
    v[4] = "1.2.3"
    v[5] = "9"
    v[6] = "9.9.9.9"
    v[7] = "99.99.99.99"
    v[8] = "99.0.99.99"
    v[9] = ""

    for(i in v)
        printf("#%d: %s => %s\n", i, v[i], inc(v[i])) | "sort | column -t"
}

function inc(s,    a, len1, len2, len3, head, tail)
{
    split(s, a, ".")

    len1 = length(a)
    if(len1==0)
        return -1
    else if(len1==1)
        return s+1

    len2 = length(a[len1])
    len3 = length(a[len1]+1)

    head = join(a, 1, len1-1)
    tail = sprintf("%0*d", len2, (a[len1]+1)%(10^len2))

    if(len2==len3)
        return head "." tail
    else
        return inc(head) "." tail
}

function join(a, x, y,    s)
{
    for(i=x; i<y; i++)
        s = s a[i] "."
    return s a[y]
}

$ chmod +x inc.awk
$ ./inc.awk
#1:  1.2.3.4      =>  1.2.3.5
#2:  1.2.3.44     =>  1.2.3.45
#3:  1.2.3.99     =>  1.2.4.00
#4:  1.2.3        =>  1.2.4
#5:  9            =>  10
#6:  9.9.9.9      =>  10.0.0.0
#7:  99.99.99.99  =>  100.00.00.00
#8:  99.0.99.99   =>  99.1.00.00
#9:  =>           -1
21 голосов
/ 01 февраля 2014

Вот еще более короткая версия, которая также поддерживает постфикс (хорошо для -SNAPSHOT)

$ cat versions
1.2.3.44
1.2.3.9
1.2.3
9
42.2-includes-postfix

$ perl -pe 's/^((\d+\.)*)(\d+)(.*)$/$1.($3+1).$4/e' < versions
1.2.3.45
1.2.3.10
1.2.4
10
42.3-includes-postfix

Объяснение

Я использовал регулярные выражения, чтобы захватить 3 части. Материал перед последней позицией, число для увеличения и материал после.

  • ((\d+\.)*) - материал от 1.1.1.1.1.
  • (\d+) - последняя цифра
  • (.*) - материал после последней цифры

Затем я использую опцию e, чтобы разрешить выражения в заменяемой части. Заметьте, что опция e \ 1 становится переменной $ 1, и вам нужно объединить переменные с помощью оператора точки.

  • $1 - группа захвата 1.1.1.1.1.
  • ($3+1) - увеличить последнюю цифру. примечание $ 2 используется в подгруппе $ 1 для получения повторного 1.
  • $4 - материал после последней цифры
21 голосов
/ 28 июня 2013

Вот еще несколько гибких вариантов. Оба принимают второй аргумент, чтобы указать, какую позицию увеличивать.

1. Простая функция

Для более предсказуемого ввода.

# Usage: increment_version <version> [<position>]
increment_version() {
 local v=$1
 if [ -z $2 ]; then 
    local rgx='^((?:[0-9]+\.)*)([0-9]+)($)'
 else 
    local rgx='^((?:[0-9]+\.){'$(($2-1))'})([0-9]+)(\.|$)'
    for (( p=`grep -o "\."<<<".$v"|wc -l`; p<$2; p++)); do 
       v+=.0; done; fi
 val=`echo -e "$v" | perl -pe 's/^.*'$rgx'.*$/$2/'`
 echo "$v" | perl -pe s/$rgx.*$'/${1}'`printf %0${#val}s $(($val+1))`/
}

# EXAMPLE   ------------->   # RESULT
increment_version 1          # 2
increment_version 1.0.0      # 1.0.1
increment_version 1 2        # 1.1
increment_version 1.1.1 2    # 1.2
increment_version 00.00.001  # 00.00.002

2. Надежная функция

Для использования со сценариями или более настраиваемых для применения в различных системах управления версиями. Он может использовать еще пару опций, но в настоящее время он работает для моих проектов с использованием последовательностей версий "major.minor [.maintenance [.build]]".

# Accepts a version string and prints it incremented by one.
# Usage: increment_version <version> [<position>] [<leftmost>]
increment_version() {
   local usage=" USAGE: $FUNCNAME [-l] [-t] <version> [<position>] [<leftmost>]
           -l : remove leading zeros
           -t : drop trailing zeros
    <version> : The version string.
   <position> : Optional. The position (starting with one) of the number 
                within <version> to increment.  If the position does not 
                exist, it will be created.  Defaults to last position.
   <leftmost> : The leftmost position that can be incremented.  If does not
                exist, position will be created.  This right-padding will
                occur even to right of <position>, unless passed the -t flag."

   # Get flags.
   local flag_remove_leading_zeros=0
   local flag_drop_trailing_zeros=0
   while [ "${1:0:1}" == "-" ]; do
      if [ "$1" == "--" ]; then shift; break
      elif [ "$1" == "-l" ]; then flag_remove_leading_zeros=1
      elif [ "$1" == "-t" ]; then flag_drop_trailing_zeros=1
      else echo -e "Invalid flag: ${1}\n$usage"; return 1; fi
      shift; done

   # Get arguments.
   if [ ${#@} -lt 1 ]; then echo "$usage"; return 1; fi
   local v="${1}"             # version string
   local targetPos=${2-last}  # target position
   local minPos=${3-${2-0}}   # minimum position

   # Split version string into array using its periods. 
   local IFSbak; IFSbak=IFS; IFS='.' # IFS restored at end of func to                     
   read -ra v <<< "$v"               #  avoid breaking other scripts.

   # Determine target position.
   if [ "${targetPos}" == "last" ]; then 
      if [ "${minPos}" == "last" ]; then minPos=0; fi
      targetPos=$((${#v[@]}>${minPos}?${#v[@]}:$minPos)); fi
   if [[ ! ${targetPos} -gt 0 ]]; then
      echo -e "Invalid position: '$targetPos'\n$usage"; return 1; fi
   (( targetPos--  )) || true # offset to match array index

   # Make sure minPosition exists.
   while [ ${#v[@]} -lt ${minPos} ]; do v+=("0"); done;

   # Increment target position.
   v[$targetPos]=`printf %0${#v[$targetPos]}d $((10#${v[$targetPos]}+1))`;

   # Remove leading zeros, if -l flag passed.
   if [ $flag_remove_leading_zeros == 1 ]; then
      for (( pos=0; $pos<${#v[@]}; pos++ )); do
         v[$pos]=$((${v[$pos]}*1)); done; fi

   # If targetPosition was not at end of array, reset following positions to
   #   zero (or remove them if -t flag was passed).
   if [[ ${flag_drop_trailing_zeros} -eq "1" ]]; then
        for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do unset v[$p]; done
   else for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do v[$p]=0; done; fi

   echo "${v[*]}"
   IFS=IFSbak
   return 0
}

# EXAMPLE   ------------->   # RESULT
increment_version 1          # 2
increment_version 1 2        # 1.1
increment_version 1 3        # 1.0.1
increment_version 1.0.0      # 1.0.1
increment_version 1.2.3.9    # 1.2.3.10
increment_version 00.00.001  # 00.00.002
increment_version -l 00.001  # 0.2
increment_version 1.1.1.1 2   # 1.2.0.0
increment_version -t 1.1.1 2  # 1.2
increment_version v1.1.3      # v1.1.4
increment_version 1.2.9 2 4     # 1.3.0.0
increment_version -t 1.2.9 2 4  # 1.3
increment_version 1.2.9 last 4  # 1.2.9.1

Очевидно, что это слишком просто для увеличения строки версии. Но я написал это потому, что мне нужны были проекты разных типов, и потому что, если скорость не является проблемой, я предпочитаю возможность многократного использования вместо настройки одного и того же кода в десятках скриптов. Я полагаю, что это только моя объектно-ориентированная сторона просочилась в мои сценарии.

11 голосов
/ 28 декабря 2011

Pure Bash:

increment_version ()
{
  declare -a part=( ${1//\./ } )
  declare    new
  declare -i carry=1

  for (( CNTR=${#part[@]}-1; CNTR>=0; CNTR-=1 )); do
    len=${#part[CNTR]}
    new=$((part[CNTR]+carry))
    [ ${#new} -gt $len ] && carry=1 || carry=0
    [ $CNTR -gt 0 ] && part[CNTR]=${new: -len} || part[CNTR]=${new}
  done
  new="${part[*]}"
  echo -e "${new// /.}"
} 

version='1.2.3.44'

increment_version $version

результат:

1.2.3.45

Строка версии разделяется и сохраняется в массиве part .Цикл идет от последней к первой части версии.Последняя часть будет увеличена и, возможно, урезана до первоначальной длины.Керри переносится на следующую часть.

1 голос
/ 29 декабря 2011

Устали от Баш?Почему бы не попробовать Perl?

$ cat versions
1.2.3.44
1.2.3.9
1.2.3
9

$ cat versions | perl -ne 'chomp; print join(".", splice(@{[split/\./,$_]}, 0, -1), map {++$_} pop @{[split/\./,$_]}), "\n";'
1.2.3.45
1.2.3.10
1.2.4
10

Конечно, не совсем соответствует требованию.

1 голос
/ 28 декабря 2011

Определение номера версии для программного проекта основано на его относительном изменении / функциональности / стадии разработки / редакции. Последующее увеличение нумерации версий и версий в идеале - это процесс, который должен выполнять человек. Однако, чтобы не догадываться о вашей мотивации для написания этого сценария, вот мое предложение.

Включите в ваш скрипт некоторую логику, которая будет делать именно то, что вы описываете в вашем требовании

"... если последний раздел после точки состоит из двух цифр, inc до 99; если только 1, то inc до 9 ..."

Предполагая, что третья позиция - это номер этапа разработки $dNum, а четвертая (последняя) позиция - номер редакции $rNum:

if  [ $(expr length $rNum) = "2" ] ; then 
    if [ $rNum -lt 99 ]; then 
        rNum=$(($rNum + 1))
    else rNum=0
         dNum=$(($dNum + 1)) #some additional logic for $dNum > 9 also needed
    fi
elif [ $(expr length $dNum) = "1" ] ; then
    ...
    ...
fi

Возможно, функция позволит наиболее краткий способ обработки всех позиций (majNum.minNum.dNum.rNum).

Вам придется разделить компоненты имени проекта и номера версии вашего имени файла в вашем скрипте, а затем создать номер версии со всеми его позициями и, наконец, восстановить имя файла с помощью чего-то вроде

new_version="$majNum.minNum.$dNum.$rNum"
new_version_file="$old_file.$new_version"

Надеюсь, что это поможет, и проверьте это обсуждение SO , а также эту запись в Википедии , если вы хотите узнать больше о соглашениях о версиях.

0 голосов
/ 13 марта 2019

Другой вариант - использовать Python. Я думаю, что это немного более читабельно, чем использование простого Bash или Perl.

function increase_version() {
    python - "$1" <<EOF
import sys
version = sys.argv[1]
base, _, minor = version.rpartition('.')
print(base + '.' + str(int(minor) + 1))
EOF
}
0 голосов
/ 28 декабря 2011

Использование только bash, wc и sed:

#! /bin/bash
for v in 1.2.3.44 1.2.3.9 1.2.3 9 1.4.29.9 9.99.9 ; do
    echo -n $v '-> '

    num=${v//./}
    let num++

    re=${v//./)(}
    re=${re//[0-9]/.}')'
    re=${re#*)}

    count=${v//[0-9]/}
    count=$(wc -c<<<$count)
    out=''
    for ((i=count-1;i>0;i--)) ; do
        out='.\'$i$out
    done

    sed -r s/$re$/$out/ <<<$num
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...