次の単純なバージョン管理スクリプトは、特定のファイルの最後のバージョン番号を見つけ、それをインクリメントし、新しく作成されたファイル(例:エディター)を使用して特定のコマンドを実行し、その後安定版に保存することを目的としています。シンプルなので、スクリプトは必要に応じて変更されるため、何もチェックされません。たとえば、結果が安定しない場合、ユーザーは最後の引数を省略できます。
ただし、現在の機能の1つの主な懸念事項は、次の実装方法です。 1のみの場合は、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.
更新:以下は私のPCで動作しますが、未解決の問題の改善または解決策が見つかった場合は更新します。
#!/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.
さまざまなソリューションが提供されていることを感謝します。それらは、視点を獲得し、比較を行うのに役立ちます。
$ 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
#!/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
次に、より柔軟なオプションをいくつか示します。どちらも2番目の引数を受け入れ、増分する位置を示します。
より予測可能な入力。
# 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
スクリプトでの使用、またはさまざまなバージョン管理システムに適用するためのより多くのカスタマイズ性。さらにいくつかのオプションを使用することもできますが、今のところ、「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
明らかに、これはバージョン文字列をインクリメントするためだけに過剰です。ただし、これは、さまざまな種類のプロジェクトが必要であり、速度が問題にならない場合は、何十ものスクリプト間で同じコードを微調整するよりも再利用性を好むため、これを記述しました。それは、オブジェクト指向の側面がスクリプトに漏れているだけだと思います。
これは、接尾辞もサポートするさらに短いバージョンです(-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
-最後の桁の後のものピュアバッシュ:
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に格納されます。ループは、バージョンの最後から最初の部分に移動します。最後の部分は増分され、おそらく元の長さに切り下げられます。キャリーは次のパートに持ち込まれます。
バッシュにうんざりしていませんか? 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
もちろん、要件に完全には準拠していません。
ソフトウェアプロジェクトのバージョン番号の決定は、その相対的な変更、機能、開発段階、リビジョンに基づいています。バージョンとリビジョン番号の結果として生じる増分は、理想的には人間が行うべきプロセスです。ただし、このスクリプトを作成する動機を推測するのではなく、ここに私の提案を示します。
要件に記述したとおりの処理を実行するロジックをスクリプトに含めます
"...ドットの後の最後のセクションが2桁の場合、incは99まで、1のみの場合は、incまで9 ..."
3番目の位置が開発段階番号であると仮定$dNum
および4番目(最後)の位置はリビジョン番号$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"
バージョン管理の規則について詳しく知りたい場合は、 this SO conversation と this wikipedia entry が役立つことを願っています。
別のオプションは、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
}
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