6

标题确实说明了一切,但我目前正在使用一个带有 case 语句的简单函数将人类可读的文件大小字符串转换为以字节为单位的大小。它工作得很好,但是对于移植到其他代码来说有点笨拙,所以我很想知道是否有任何广泛可用的命令可供 shell 脚本使用?

基本上我想获取诸如“100g”或“100gb”之类的字符串并将它们转换为字节。

我目前正在执行以下操作:

to_bytes() {
    value=$(echo "$1" | sed 's/[^0123456789].*$//g')
    units=$(echo "$1" | sed 's/^[0123456789]*//g' | tr '[:upper:]' '[:lower:]')

    case "$units" in
        t|tb)   let 'value *= 1024 * 1024 * 1024 * 1024'    ;;
        g|gb)   let 'value *= 1024 * 1024 * 1024'   ;;
        m|mb)   let 'value *= 1024 * 1024'  ;;
        k|kb)   let 'value *= 1024' ;;
        b|'')   let 'value += 0'    ;;
        *)
                value=
                echo "Unsupported units '$units'" >&2
        ;;
    esac

    echo "$value"
}

对于我认为对于处理文件的脚本来说相当普遍的东西来说,这似乎有点矫枉过正;足够普遍,以至于可能存在一些东西可以更快地做到这一点。

如果没有广泛可用的解决方案(即大多数 unix 和 linux 风格),那么我仍然会感谢任何优化上述功能的技巧,因为我想让它更小更容易重用。

4

6 回答 6

8

man numfmt

# numfmt --from=iec 42 512K 10M 7G 3.5T
42
524288
10485760
7516192768
3848290697216

# numfmt --to=iec 42 524288 10485760 7516192768 3848290697216
42
512K
10M
7.0G
3.5T
于 2019-03-13T11:21:42.587 回答
4
toBytes() {
 echo $1 | echo $((`sed 's/.*/\L\0/;s/t/Xg/;s/g/Xm/;s/m/Xk/;s/k/X/;s/b//;s/X/ *1024/g'`))
}
于 2014-06-18T15:48:44.227 回答
2

这是我写的东西。它支持kKBKiB。(它不区分后缀的 2 次方和 10 次方,例如 1KB = 1000 字节,1KiB = 1024 字节。)

#!/bin/bash

parseSize() {(
    local SUFFIXES=('' K M G T P E Z Y)
    local MULTIPLIER=1

    shopt -s nocasematch

    for SUFFIX in "${SUFFIXES[@]}"; do
        local REGEX="^([0-9]+)(${SUFFIX}i?B?)?\$"

        if [[ $1 =~ $REGEX ]]; then
            echo $((${BASH_REMATCH[1]} * MULTIPLIER))
            return 0
        fi

        ((MULTIPLIER *= 1024))
    done

    echo "$0: invalid size \`$1'" >&2
    return 1
)}

笔记:

  • 利用 bash 的=~正则表达式运算符,它将匹配项存储在名为BASH_REMATCH.
  • 注意函数体周围巧妙隐藏的括号。他们在那里是为了防止shopt -s nocasematch泄漏到功能之外。
于 2013-07-12T13:30:48.773 回答
0

好的,听起来好像没有任何内置或广泛可用的东西,这很遗憾,所以我尝试减小函数的大小并想出一些只有 4 行长的东西,虽然它很漂亮复杂的四行!

我不确定它是否适合作为我最初问题的答案,因为它并不是我所说的最简单的方法,但我想提出它以防有人认为它是一个有用的解决方案,并且它确实具有优势真的很短。

#!/bin/sh
to_bytes() {
    units=$(echo "$1" | sed 's/^[0123456789]*//' | tr '[:upper:]' '[:lower:]')
    index=$(echo "$units" | awk '{print index ("bkmgt kbgb  mbtb", $0)}')
    mod=$(echo "1024^(($index-1)%5)" | bc)
    [ "$mod" -gt 0 ] && 
        echo $(echo "$1" | sed 's/[^0123456789].*$//g')"*$mod" | bc
}

为了快速总结它的工作原理,它首先从给定的字符串中去除数字并强制为小写。然后它使用awk从有效后缀的结构化字符串中获取扩展的索引。需要注意的是,字符串被排列为 5 的倍数(因此如果添加更多扩展名,则需要加宽),例如 k 和 kb 分别位于索引 2 和 7 处。然后将索引减一并以五为模,因此 k 和 kb 都变为 1,m 和 mb 变为 2,依此类推。然后将其用于提高 1024 作为获取大小(以字节为单位)的幂。如果扩展名无效,这将解析为零值,并且 b 的扩展名(或无)将评估为 1。只要 mod 大于零,输入字符串将减少为仅数字部分并乘以修饰符以获得最终结果。

如果我使用 PHP、Java 等语言,这实际上是我最初可能会解决的问题,将它放在一个 shell 脚本中只是有点奇怪。

不过,我仍然非常感谢任何简化!

于 2013-07-12T15:23:47.977 回答
0

不知道这样行不行:

awk 'BEGIN{b=1;k=1024;m=k*k;g=k^3;t=k^4}
/^[0-9.]+[kgmt]?b?$/&&/[kgmtb]$/{
    sub(/b$/,"")
        sub(/g/,"*"g)
        sub(/k/,"*"k)
        sub(/m/,"*"m)
        sub(/t/,"*"t)
"echo "$0"|bc"|getline r; print r; exit;}
{print "invalid input"}'
  • 这仅处理单行输入。如果需要多行,请删除exit
  • 这仅检查 pattern[kgmt]和 optional b。例如kib, mib会失败。目前也仅用于小写。

例如:

kent$  echo "200kb"|awk 'BEGIN{b=1;k=1024;m=k*k;g=k^3;t=k^4}                                                                                                                
/^[0-9.]+[kgmt]?b?$/&&/[kgmtb]$/{
    sub(/b$/,"")
        sub(/g/,"*"g)
        sub(/k/,"*"k)
        sub(/m/,"*"m)
        sub(/t/,"*"t)
"echo "$0"|bc"|getline r
print r; exit
}{print "invalid input"}'
204800
于 2013-07-12T13:47:42.753 回答
0

另一种变体,使用更简单的 T/G/M/K 解析器添加对十进制值的支持,以用于您可能从更简单的 Unix 程序中找到的输出。

to_bytes() {
value=$(echo "$1" | sed -e 's/K//g' | sed -e 's/M//g' | sed -e 's/G//g' | sed -e 's/T//g' )
units=$(echo -n "$1" | grep -o .$ )
    case "$units" in
        T)   value=$(bc <<< "scale=2; ($value * 1024 * 1024 * 1024 * 1024)")    ;;
        G)   value=$(bc <<< "scale=2; ($value * 1024 * 1024 * 1024)")   ;;
        M)   value=$(bc <<< "scale=2; ($value * 1024 * 1024)")  ;;
        K)   value=$(bc <<< "scale=2; ($value * 1024)") ;;
        b|'')   let 'value += 0'    ;;
        *)
                value=
                echo "Unsupported units '$units'" >&2
        ;;
    esac
echo "$value"
}
于 2017-07-27T07:20:45.967 回答