如何在长行中每四个字符插入一个空格?

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)

  • 只是好奇,'&amp;' 有什么作用?哦,它是“刚好匹配的东西”的代名词。傻我。 (9认同)
  • @Anubis `'s/.\{4\}/&amp; /g;s/ $//'` (5认同)
  • 应该注意的是,如果字符串中还有一个字符,这也会在末尾添加一个空格,这可能是不可取的 (3认同)

ken*_*orb 29

您可以使用以下简单示例:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,对于当前版本的 GNU `fold`,它不适用于多字节字符(如 `echo €€€€€€€€ | fold -w4 | paste -sd' ' -` in UTF-8 )。 (3认同)

mr.*_*tic 5

仅在 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 版本有能力,那么以上都是多字节字符安全的。
  • shellprintf重用其格式字符串,直到它用完参数,所以它一次只吞下 4 个参数,并处理尾随参数(因此不需要边缘情况,不像这里的其他一些答案可能是错误的)
  • BASH_REMATCH[0] 是整个匹配的字符串,所以只输出从索引 1 开始
  • 使用printf -v myvar ...代替存储到变量myvar(取决于通常的读取循环/子外壳行为)
  • printf "\n"如果需要添加

zsh如果您使用数组match[]而不是 ,则可以使上述工作BASH_REMATCH[],并从所​​有索引中减去 1,因为zsh不会在整个匹配项中保留 0 元素。


Sté*_*las 5

仅与zsh

\n\n
str=foobarbazblargblurg\n\nset -o extendedglob\nprintf \'%s\\n\' ${str//(#m)????/$MATCH }\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者

\n\n
printf \'%s%s%s%s \' ${(s::)str}\n
Run Code Online (Sandbox Code Playgroud)\n\n

仅包含ksh93

\n\n
printf \'%s\\n\' "${str//????/\\0 }"\n
Run Code Online (Sandbox Code Playgroud)\n\n

仅适用于任何 POSIX shell(如果输入长度是 4 的倍数,也避免尾随空格):

\n\n
out=\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

\n\n
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上述情况,但在涉及某些其他类型的零宽度或双宽度字符时也可能有所帮助:

\n\n
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