2016-03-21 38 views
1

我有一个大多数版本号的目录,并且我试图确定在另一个特定版本之前进行版本排序的目录。从bash中的排序列表中选择

这里的背景,因为担心这是一个XY问题,这些版本号是从web内容的git标签派生的,我们将该网站的虚拟主机的public_html目录符号链接到版本化的目录。

它看起来是这样的:

drwxr-xr-x 8 graham www 17 Oct 17 2013 v2.0.10 
drwxr-xr-x 8 graham www 17 Oct 17 2013 v2.0.11 
drwxrwxr-x 8 graham www 17 Aug 29 2013 v2.0.8 
drwxr-xr-x 8 brian www 17 Oct 17 2013 v2.0.9 
... 
drwxr-xr-x 9 graham www 21 Aug 5 2015 v4.19.0 
drwxr-xr-x 10 graham www 19 Dec 17 2014 v4.2.0 
drwxr-xr-x 9 brian www 21 Aug 10 2015 v4.20.0 
drwxr-xr-x 9 brian www 21 Aug 10 2015 v4.20.0-fail 
drwxr-xr-x 9 graham www 21 Aug 11 2015 v4.20.1 
... 
drwxr-xr-x 9 graham www 19 Mar 14 11:00 v4.35.0 
drwxr-xr-x 9 graham www 19 Mar 14 13:28 v4.36.0 
drwxr-xr-x 9 graham www 19 Mar 16 10:58 v4.36.1 
lrwxr-xr-x 1 graham www 11 Mar 16 11:13 public_html -> v4.36.1 

符号连接可能不总是在最近的版本指向(例如,如果我们有备份的变化了)。我的目标是找到在public_html指向之前版本排序的正确命名的目录(即我们跳过*-fail之类)。

我在FreeBSD的,所以我stat命令很好地工作是这样的:

read target <<<"$(stat -f'%Y' "${base}/public_html")" 

如果您在Linux中的时候,我猜想你可以得到的是这样的效果相同:

target="$(stat -c '%N' "${base}/public_html")" 
target="${target#* -> ?}"; target="${target%?}" 

但是从这里,我不知道如何收集版本号。请注意,我在FreeBSD中,所以我的sort命令没有-V选项。我试过两种方法。

首先是处理上的目录的内容的while循环:

shopt -s extglob 
while read this; do 
    [[ "$this" = "$target" ]] && break 
    previous="$this" 
done < $(ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -n -t . -k1,1 -k2,2 -k3,3) 

第二是非常相似,解析阵列代替:

shopt -s extglob 
a=($(ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -n -t . -k1,1 -k2,2 -k3,3)) 
for ((n=0; n<${#a[@]}; n++)); do 
    [[ "${a[$n]}" = "$target" ]] && break 
    previous="${a[$n]}" 
done 

当这两个倒下是sort,其中第一个字段看起来没有被排序。我怀疑这是因为它不是数字(一开始是“v”,用“。”分隔)。有没有办法指定多个分隔符,或以其他方式“忽略”v

还是有更好的方法来处理这个问题,比使用外部sort命令,它有非便携式选项?或许是内部的东西?

谢谢。

回答

0

也许不是完整解决方案,但你也许可以用下面的闪避:

ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -t . -k1,1 -k2,2n -k3,3n 

这里的区别在于,-n数字字段只适用于第二和第三场,所以你会继续在第一个字段按字母顺序排序。

这不会帮助你在跳从v9.x.xv10.x.x,但你从v2v4在一点点三年大变样,也许这是“足够好”。

ALSO:

  • 你可能会分两个阶段运行此。第一阶段确定最近的主要版本号,如major=$(ls -1d v[0-9]* | tail -1); major=${major%%.*}",然后后续排序基于a=$(ls ${major}.+([0-9]).+([0-9]))。然后你可以安全地排序数字字段。
  • a bash implementation of qsort,你可能可以适应,以便做一些完全内部的东西。它需要一些黑客来使它与阵列协同工作。
0

您可以通过子场分离阿尔法前缀,数字排序,并合并回

$ ls -1 v* | sed 's/^v/v./' | sort -t. -k2,2n -k3,3n -k4n | sed 's/^v\./v/' 

v2.0.8 
v2.0.9 
v2.0.10 
v2.0.11 
v4.2.0 
v4.19.0 
v4.20.0 
v4.20.0-fail 
v4.20.1 
v4.26.1 
v4.35.0 
v4.36.0 
v10.0.1 

注意,我说的V10。*,以确认它是面向未来的。

+0

你在哪里看到'-v'选项一起使用?这是缺少版本选项的解决方法。 – karakfa

+0

对不起,我的错。今天下午无法阅读。 –

0

这纯巴什(除readlink)解决方案做了线性搜索,而不是排序:

VERSION_RX='^v([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)$' 

target=$(readlink "$base/public_html") 

# target == v1.22.333 -> target_key == 000000001_000000022_000000333 
[[ $target =~ $VERSION_RX ]] || exit 1 
printf -v target_key '%09d_%09d_%09d' \ 
    "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" 

pred_dir= 
pred_key= 

for path in "$base"/v*.*.* ; do 
    dir=${path##*/} 
    [[ $dir == "$target" ]] && continue 
    [[ $dir =~ $VERSION_RX ]] || continue 
    printf -v key '%09d_%09d_%09d' \ 
     "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" 

    [[ $key > $target_key ]] && continue 

    if [[ -z $pred_key || $key > $pred_key ]] ; then 
     pred_dir=$dir 
     pred_key=$key 
    fi 
done 

echo "$pred_dir"