【问题标题】:How can I truncate a line of text longer than a given length?如何截断比给定长度更长的文本行?
【发布时间】:2020-12-05 10:00:01
【问题描述】:

您将如何删除 x 个字符后的所有内容?例如,剪切 15 个字符后的所有内容并添加 ...

This is an example sentence 应该变成This is an exam...

【问题讨论】:

    标签: string bash shell string-length truncation


    【解决方案1】:

    GnuTools head 可以使用字符而不是行:

    head -c 15 <<<'This is an example sentence'
    

    虽然考虑到head -c 只处理字节,所以这与UTF-8 变音符号ü 等多字节字符不兼容。

    Bash 内置字符串索引的工作原理:

    str='This is an example sentence'
    echo "${str:0:15}"
    

    输出:

    This is an exam
    

    最后还有一些适用于 ksh、dash、zsh 的东西……:

    printf '%.15s\n' 'This is an example sentence'
    

    甚至以编程方式:

    n=15
    printf '%.*s\n' $n 'This is an example sentence'
    

    如果您使用的是 Bash,您可以直接将 printf 的输出分配给一个变量并保存一个子 shell 调用:

    trim_length=15
    full_string='This is an example sentence'
    printf -v trimmed_string '%.*s' $trim_length "$full_string"
    

    【讨论】:

    • cut 我猜也是一个选项,它也可以处理多字节字符
    【解决方案2】:

    Awk 也可以做到这一点:

    $ echo 'some long string value' | awk '{print substr($0, 1, 15) "..."}'
    some long strin...
    

    在 awk 中,$0 是当前行。 substr($0, 1, 15)$0 中提取字符 1 到 15。尾随 "..." 附加三个点。

    【讨论】:

      【解决方案3】:

      使用cut:

      echo "This is an example sentence" | cut -b1-15
      This is an exam
      

      【讨论】:

      • 这应该是公认的答案。
      【解决方案4】:

      你可以试试:

      echo 'some long string value' | sed 's/\(.\{15\}\).*/\1.../'
      

      输出:

      some long strin...
      

      【讨论】:

        【解决方案5】:

        使用 Bash Shell 扩展(无外部命令)

        如果您不关心 shell 的可移植性,您可以完全在 Bash 中使用 printf builtin 中的多个不同的 shell expansions 来完成此操作。这避免了对外部命令的攻击。例如:

        trim () {
            local str ellipsis_utf8
            local -i maxlen
        
            # use explaining variables; avoid magic numbers        
            str="$*"
            maxlen="15"
            ellipsis_utf8=$'\u2026'
        
            # only truncate $str when longer than $maxlen
            if (( "${#str}" > "$maxlen" )); then
              printf "%s%s\n" "${str:0:$maxlen}" "${ellipsis_utf8}"
            else
              printf "%s\n" "$str"
            fi
        }
        
        trim "This is an example sentence." # This is an exam…
        trim "Short sentence."              # Short sentence.
        
        trim "-n Flag-like strings."        # Flag-like strin…
        trim "With interstitial -E flag."   # With interstiti…
        

        您也可以通过这种方式循环浏览整个文件。给定一个包含上述相同句子的文件(每行一个),您可以使用read builtin's default REPLY variable,如下所示:

        while read; do
            trim "$REPLY"
        done < example.txt
        

        这种方法是否更快或更容易阅读尚有争议,但它是 100% Bash 并且无需分叉或子外壳即可执行。

        【讨论】:

        • 在对 GNU 邮件列表进行了一些讨论之后,事实证明 Bash 对内置 echo 的选项解析具有一些已知的 POSIX 强加限制。具体来说,它不像大多数其他内置函数那样处理选项结束标志。因此,如果 trim() 的串联参数可以以-n-e-E开始,则使用内置 printf 是更好的选择。
        【解决方案6】:

        Todd 实际上有一个很好的答案,但是我选择对其进行一些更改以使功能更好并删除不必要的部分:p

        trim() {
            if (( "${#1}" > "$2" )); then
              echo "${1:0:$2}$3"
            else
              echo "$1"
            fi
        }
        

        在此版本中,较长字符串上的附加文本由第三个参数选择,最大长度由第二个参数选择,文本本身由第一个参数选择。

        不需要变量:)

        【讨论】:

        • 这与 ksh、zsh、dash 和更多的 shell 兼容,因为它不是 Bashism 实现:trim(){ printf '%.*s' $2 "$1";}。如果你只想要 Bash,但速度更快;你可以这样做:printf -v trimmed_string '%.*s' $trim "$full_string"
        猜你喜欢
        • 2010-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-25
        • 1970-01-01
        • 2017-06-18
        • 1970-01-01
        相关资源
        最近更新 更多