如何截断超过给定长度的文本行?

18

如何删除指定数量的字符后的所有内容?例如,删除15个字符后的所有内容并添加...

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

6个回答

24

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"

1
“cut” 也是一个选项,我猜它也可以处理多字节字符。 - oguz ismail
1
${str:0:15}非常好,谢谢! - Garret Wilson

11

使用 sed

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

输出:

some long strin...

这种解决方案的优点是短字符串不会添加...尾随:

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

输出:

short string

所以它是适用于所有大小输出的解决方案。


8
使用cut命令:
echo "This is an example sentence" | cut -c1-15
This is an exam

这包括字符(用于处理多字节字符)1-15,参见cut(1)
     -b, --bytes=LIST
            select only these bytes

     -c, --characters=LIST
            select only these characters

不适用于包含换行符的字符串。 - Gibz

3

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

Todd 提供了不错的答案,但我稍作改动,使函数更好并去除了不必要的部分 :p

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

在这个版本中,追加到较长字符串的文本由第三个参数选择,最大长度由第二个参数选择,而文本本身由第一个参数选择。
无需使用变量 :)

1
这个代码片段兼容ksh、zsh、dash等许多shell,因为它不是Bashism实现:trim(){ printf '%.*s' $2 "$1";}。如果你只想在Bash中使用并且速度更快,可以这样做:printf -v trimmed_string '%.*s' $trim "$full_string" - Léa Gris
你需要如何调用它的示例:trim "My Long String" <maxLen> "..." - automorphic

2
使用Bash Shell扩展(无外部命令)
如果您不关心shell的可移植性,可以完全在Bash中使用许多不同的shell扩展,在printf 内置命令中完成此操作。这避免了调用外部命令。例如:
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内置函数的默认REPLY变量,如下所示:
while read; do
    trim "$REPLY"
done < example.txt

这种方法是否更快、更易读,存在争议,但它是100%的Bash,并且执行时不需要forks或子shell。

在GNU邮件列表上进行了一些讨论,结果发现Bash的echo内置选项解析存在一些已知的POSIX强制限制。具体来说,它不像大多数其他内置命令那样处理选项结束标志。因此,如果trim()的连接参数可以以“-n”、“-e”或“-E”开头,则使用printf内置命令是更好的选择。 - Todd A. Jacobs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接