xen*_*ide 44 command-line shell text-processing
我有一行很长的行,我想每 4 个字符插入一个空格,在单行实心文本上插入一个空格以使其更易于阅读,最简单的方法是什么?我也应该能够从管道输入行。例如
echo "foobarbazblargblurg" | <some command here>
Run Code Online (Sandbox Code Playgroud)
给
foob arba zbla rgbl urg
Run Code Online (Sandbox Code Playgroud)
dog*_*ane 74
使用 sed 如下:
$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
Run Code Online (Sandbox Code Playgroud)
ken*_*orb 29
您可以使用以下简单示例:
$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
Run Code Online (Sandbox Code Playgroud)
仅在 bash 中,没有外部命令:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
Run Code Online (Sandbox Code Playgroud)
或作为单线管道版本:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
Run Code Online (Sandbox Code Playgroud)
其工作方式是将字符串的每个字符转换为“(.)”以进行正则表达式匹配和捕获=~
,然后只从BASH_REMATCH[]
数组中输出捕获的表达式 ,根据需要进行分组。保留前导/尾随/中间空格,删除周围的引号"${BASH_REMATCH[@]:1}"
以省略它们。
这里它被封装在一个函数中,如果没有参数,这个函数将处理它的参数或读取标准输入:
function fmt4() {
while IFS= read -r str; do
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}
$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg
Run Code Online (Sandbox Code Playgroud)
您可以轻松地参数化计数以相应地调整格式字符串。
添加了尾随空格,printf
如果有问题,请使用两个s 而不是一个:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Run Code Online (Sandbox Code Playgroud)
第一个printf
打印(最多)前 4 个字符,第二个有条件地打印所有其余字符(如果有),并用前导空格分隔组。测试是针对 5 个元素而不是 4 个元素来解释第零个元素。
笔记:
printf
的%c
可被用来代替%s
,%c
(也许)使得意图清晰,但它不是多字节字符安全。如果您的 bash 版本有能力,那么以上都是多字节字符安全的。printf
重用其格式字符串,直到它用完参数,所以它一次只吞下 4 个参数,并处理尾随参数(因此不需要边缘情况,不像这里的其他一些答案可能是错误的)BASH_REMATCH[0]
是整个匹配的字符串,所以只输出从索引 1 开始printf -v myvar ...
代替存储到变量myvar
(取决于通常的读取循环/子外壳行为)printf "\n"
如果需要添加zsh
如果您使用数组match[]
而不是 ,则可以使上述工作BASH_REMATCH[]
,并从所有索引中减去 1,因为zsh
不会在整个匹配项中保留 0 元素。
仅与zsh
:
str=foobarbazblargblurg\n\nset -o extendedglob\nprintf \'%s\\n\' ${str//(#m)????/$MATCH }\n
Run Code Online (Sandbox Code Playgroud)\n\n或者
\n\nprintf \'%s%s%s%s \' ${(s::)str}\n
Run Code Online (Sandbox Code Playgroud)\n\n仅包含ksh93
:
printf \'%s\\n\' "${str//????/\\0 }"\n
Run Code Online (Sandbox Code Playgroud)\n\n仅适用于任何 POSIX shell(如果输入长度是 4 的倍数,也避免尾随空格):
\n\nout=\nwhile true; do\n case $str in\n (?????*)\n new_str=${str#????}\n out=$out${str%"$new_str"}\' \'\n str=$new_str\n ;;\n (*)\n out=$out$str\n break\n esac\ndone\nprintf \'%s\\n\' "$out"\n
Run Code Online (Sandbox Code Playgroud)\n\n现在,这是针对角色的。如果您想在字素簇上执行此操作(例如,要 break St\xc3\xa9phane
,写为$\'Ste\\u0301phane\'
、 asSt\xc3\xa9p hane
和 not Ste phan e
),请使用zsh
:
set -o rematchpcre\nstr=$\'Ste\\u301phane\' out=\nwhile [[ $str =~ \'(\\X{4})(.+)\' ]] {\n out+="$match[1] " str=$match[2]\n}\nout+=$str\nprintf \'%s\\n\' $out\n
Run Code Online (Sandbox Code Playgroud)\n\n使用 ksh93,您也可以按显示宽度进行中断,这适用于St\xc3\xa9phane
上述情况,但在涉及某些其他类型的零宽度或双宽度字符时也可能有所帮助:
str=$\'Ste\\u301phane\' out=\nwhile\n start=${ printf %L.4s. "$str"; }\n start=${start%.}\n [ "$start" != "$str" ]\ndo\n out+="$start " str=${str#"$start"}\ndone\nout+=$str\nprintf \'%s\\n\' "$out"\n
Run Code Online (Sandbox Code Playgroud)\n