您好,我想知道是否有一种方法可以只使用头、尾和管道(以及最终的重定向)来提取和输出字符串的开头、中间字符和结尾
示例:给定此字符串: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 个字节,不为下一个head
和tail
命令留下任何可读取的内容。
使用 GNU 或 ksh93 时head
:
[pid 10293] read(0, "SHOW", 4) = 4
另请注意head
,tail
使用-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
或者使用最新版本的ksh93
、bash
、zsh
或mksh
:
slice=${string:0:4}${string:13:4}${string: -6}
POSIXly:
tmp=${string#?????????????}
slice=${string%"${string#????}"}${tmp%"${tmp#????}"}${string#"${string%????}"}
尽管如此C可能会建议。该-c
选项被添加到一些head
实现中以与-c
of保持一致tail
,并且早在字符可以由多个字节组成的概念之前就-c
被添加了。tail
答案2
假设您的head
andtail
支持该-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