仅使用头部和尾部提取字符串的一部分

仅使用头部和尾部提取字符串的一部分

您好,我想知道是否有一种方法可以只使用头、尾和管道(以及最终的重定向)来提取和输出字符串的开头、中间字符和结尾

示例:给定此字符串:SHOWpijfirefjTHISezpijSTRING,命令应输出 'SHOWTHISSTRING'

我尝试过类似的东西 (head -c 4 mdp > /dev/tty) | (tail -c +13 mdp | head -c 4 > /dev/tty) | (tail -c 6 mdp > /dev/tty) 2>&1

但它并不总是返回相同的结果,并且可能给出无序的结果

答案1

您可以使用head支持非标准-c选项的实现,并且足够聪明,在要求输出固定数量的字节时不会从输入中读取超出需要的内容:

string='SHOWpijfirefjTHISezpijSTRING'
printf %s "$string" | {
  head -c 4
  head -c 9 > /dev/null
  head -c 4
  tail -c 6
}

了解它如何与 GNUhead或 的head内置函数一起工作ksh93,但不能head与 busybox 一起工作。如果你运行strace -fe read busybox sh ./that-script,你会看到:

[pid  7739] read(0, "SHOWpijfirefjTHISezpijSTRING", 4096) = 28

第一个head通过读取整个块消耗所有输入并输出其中的 4 个字节,不为下一个headtail命令留下任何可读取的内容。

使用 GNU 或 ksh93 时head

[pid 10293] read(0, "SHOW", 4)          = 4

另请注意headtail使用-c字节而不是字符1,因此只能用于报告固定数量的字符,并且文本编码为每个字符一个字节。

大多数现代 shell 都有内置运算符来根据字符位置对字符串进行切片。

例如,在 zsh 或 yash 中:

slice=${string[1,4]}${string[14,17]}${string[-4,-1]}

在 zsh 中,可以缩短为:

slice=$string[1,4]$string[14,17]$string[-6,-1]

你可以就地切掉一些部分:

$ string[5,13]= string[9,-7]=
$ print -r -- $string
SHOWTHISSTRING

或者使用最新版本的ksh93bashzshmksh

slice=${string:0:4}${string:13:4}${string: -6}

POSIXly:

tmp=${string#?????????????}
slice=${string%"${string#????}"}${tmp%"${tmp#????}"}${string#"${string%????}"}

尽管如此C可能会建议。该-c选项被添加到一些head实现中以与-cof保持一致tail,并且早在字符可以由多个字节组成的概念之前就-c被添加了。tail

答案2

假设您的headandtail支持该-c选项,您的字符串只不过是没有多字节字符的简单 ASCII,并且您的 shell 支持<<<此处的字符串构造(bash 和 zsh 都支持),您可以执行以下操作:

$ string=SHOWpijfirefjTHISezpijSTRING
$ printf '%s%s%s\n' "$(head -c 4 <<<"$string")" \
                    "$(head -c 17 <<<"$string" | tail -c 4)" \
                    "$( tail -c 5 <<<"$string")"
SHOWTHISRING

或者,如果您不想printf

$ string=SHOWpijfirefjTHISezpijSTRING
$ head -c 4 <<<"$string"; \
  head -c 17 <<<"$string" | tail -c 4; \
  tail -c 5 <<<"$string"; 
SHOWTHISRING

相关内容