从 Bash 中的 ls 输出中提取带小数点的数字

从 Bash 中的 ls 输出中提取带小数点的数字

我想在 Ubuntu 14.04.1 64 位 LTS 上的 Bash 中提取以“hsli”开头并以“.h5”结尾的文件名的数字部分。我的ls -l hsli*输出如下:

-rwxrwxrwx 1 ongun ongun 31392 Feb 26 13:04 hsli0.03.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 13:44 hsli0.042.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 14:24 hsli0.054.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 15:03 hsli0.066.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 15:42 hsli0.078.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 16:22 hsli0.09.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 17:02 hsli0.102.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 17:36 hsli0.114.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 17:58 hsli0.126.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 18:20 hsli0.138.h5
-rwxrwxrwx 1 ongun ongun 31392 Feb 26 18:42 hsli0.15.h5

它们已经按升序排列,经过一些操作后,我可以使用以下命令获取第一个文件的文件名。命令和输出如下:

$ ls -l hsli* | head -1 | rev | cut -f 1 -d " " | rev 
hsli0.03.h5

现在我的目标是从这里提取0.03,我该怎么做呢?我不熟悉正则表达式,这似乎是一个困难的情况,因为文件名中有 2 个点。

答案1

如果没有ls,因为无论如何你只是用 shell glob 填充它的列表,所以你可以像这样删除中间人:

glob_hsli()(IFS=.;set +f
    set -f -- '' hsli*.*.h5
    for h5 do case ${h5#*.}  in
        (*[!0-9]*.*|.*|'') : ;;
        (*) set $h5 "${1:-0}";
        shift $((3>>($2>$4)));;
    esac;done
    printf "0.%d\n" "${1:?No Match Found!}"
)

不带参数调用它,它将全局化您的文件,并且仅打印当前目录结果中hsli*第一个出现的中间部分,或者如果它不能这样做,它将返回错误并打印到 stderr 的有意义的错误消息。*.string.h5

答案2

Bash 使得应用像这样的转换相对容易剥离前缀和后缀到数组的元素。

shopt -s nullglob                  # if there are no matches, produce an empty list
versions=(hsli*.h5)                # list matches
versions=("${versions[@]#hsli}")   # strip prefix
versions=("${versions[@]%.h5}")    # strip suffix
printf '%s\n' "${versions[@]}"     # print one version per line
for v in "${versions[@]}"; do      # execute a command on each version
  somecommand "$v"
done

请注意,版本(如果是这样的话)按字典顺序排序,因此 eg0.9位于0.10.如果您想要数字顺序并且您有足够新的 GNU coreutils 版本,则可以使用sort -Vto sort 0.9before 0.10。鉴于您的文件名不包含空格或通配符,您可以使用以下命令对它们进行排序

versions=($(printf '%s\n' "$versions[@]" | sort -V))

相关内容