从bash

时间:2016-03-21 16:41:17

标签: bash sorting

我有一个主要是版本号的目录,我正在尝试确定在另一个特定版本之前进行版本排序的目录。

这里的背景,由于担心这会导致XY问题,这些版本号是从Web内容的git标签派生的,我们将网站虚拟主机的public_html目录符号链接到版本化目录。 / p>

它看起来像这样:

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

符号链接可能并不总是指向最新版本(例如,如果我们不得不退出更改)。我的目标是找到正确命名的目录(即我们跳过*-fail等),该目录在public_html指向的版本之前排序。

我在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命令更好的方法来处理这个问题,该命令具有非便携式选项?也许是bash内在的东西?

感谢。

3 个答案:

答案 0 :(得分:0)

也许不是完整的解决方案,但您可以通过以下方式逃脱:

public class ConnectedFragment extends Fragment {

private StateModifier stateModifier;
private IpAddressChangedListener ipAddressChangedListener;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.connected_fragment_layout, container, false);

    root.findViewById(R.id.remote_control_button).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            stateModifier.remoteControl();
        }
    });

    root.findViewById(R.id.sounds_button).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            stateModifier.soundsControl();
        }
    });

    final EditText editText = (EditText)root.findViewById(R.id.ip_edit_text);

    root.findViewById(R.id.change_ip_button).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ipAddressChangedListener.onIpChangedListener(editText.getText().toString());
        }
    });

    return root;
}

public void setStateModifier(StateModifier stateModifier) {
    this.stateModifier = stateModifier;
}

public void setIpAddressChangedListener(IpAddressChangedListener ipAddressChangedListener) {
    this.ipAddressChangedListener = ipAddressChangedListener;
}
}

这里的区别在于,数字字段的ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -t . -k1,1 -k2,2n -k3,3n 仅应用于第二个和第三个字段,因此您将在第一个字段上按字母顺序继续排序。

这对您从-n跳转到v9.x.x没有帮助,但是当您在不到三年的时间内从v10.x.x转到v2时,也许它“足够好”。

ALSO:

  • 您可以分两个阶段运行。第一阶段使用v4之类的内容确定最新的主要版本号,然后后续排序基于major=$(ls -1d v[0-9]* | tail -1); major=${major%%.*}"。然后,您可以安全地对数字字段进行排序。
  • 你可以调整a bash implementation of qsort以便制作完全内在于bash的东西。它需要一些黑客才能使它与你的阵列一起使用。

答案 1 :(得分: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。*来验证它是未来的证明。

答案 2 :(得分:0)

这个纯粹的Bash(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"