I've got a long line that I want to insert a space every 4 characters, on a single lone line of solid text to make it easier to read, what's the simplest way to do this? also I should be able to input the line from a pipe. e.g.

echo "foobarbazblargblurg" | <some command here>


foob arba zbla rgbl urg

8 Answers 8


Use sed as follows:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
  • 1
    cursing that was so close to the sed I tried first I could kick myself. Jan 17, 2011 at 14:29
  • 9
    Just curious, what's the '&' accomplish? Oh, it's a standin for 'the thing what just matched'. Silly me. Aug 27, 2012 at 5:25
  • 3
    it should be noted that this adds a space at the end as well if there is one more character in the string, which might not be desirable
    – Anubis
    Apr 19, 2018 at 17:07
  • 8
    @Anubis 's/.\{4\}/& /g;s/ $//' May 23, 2019 at 15:21
  • 1
    @Owl: Perl regexes are probably opposite of sed, you probably need bare {4} to act as a counted repeat modifier, instead of \{4\}. Same if you use sed -E (extended regexps). Nov 19, 2022 at 11:42

You can use the following simple example:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
  • Very nice...I think this is even better than the sed answer. I didn't know about fold before.
    – Wildcard
    Jan 28, 2016 at 19:18
  • 4
    Unfortunately, with current versions of GNU fold, it doesn't work with multi-byte characters (as in echo €€€€€€€€ | fold -w4 | paste -sd' ' - in UTF-8). May 5, 2017 at 15:26

In bash only, no external commands:

[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"

or as a one-line pipe version:

echo foobarbazblargblurg | 
  { IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }

The way this works is to convert each character of the string to a "(.)" for regex match and capture with =~, then just output the captured expressions from BASH_REMATCH[] array, grouped as required. Leading/trailing/intermediate spaces are preserved, remove the quotes around "${BASH_REMATCH[@]:1}" to omit them.

Here it is wrapped up in a function, this one will process its arguments or read stdin if there are no arguments:

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 

You can easily parameterise the count to adjust the format string accordingly.

A trailing space is added, use two printfs instead of one if that's a problem:

printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"

The first printf prints (up to) the first 4 characters, the second conditionally prints all the rest (if any) with a leading space to separate the groups. The test is for 5 elements not 4 to account for the zeroth element.


  • shell printf's%c could be used instead of %s, %c (maybe) makes the intent clearer, but it's not multi-byte character safe. If your version of bash is capable, the above is all multi-byte character safe.
  • shell printf reuses its format string until it runs out of arguments, so it just gobbles up 4 arguments at a time, and handles the trailing arguments (so no edge cases needed, unlike some of the other answers here which are arguably wrong)
  • BASH_REMATCH[0] is the entire matched string, so only output starting from index 1
  • use printf -v myvar ... instead to store to a variable myvar (subject to usual read-loop/subshell behaviour)
  • add printf "\n" if required

You can make the above work in zsh if you use the array match[] instead of BASH_REMATCH[], and subtract 1 from all the indexes as zsh doesn't keep a 0 element with the entire match.

  • +1 Because this should have been the accepted answer but as always, fear of REGEX keeps people from learning what the shell can actually do.
    – Yokai
    Apr 18, 2021 at 17:28
  • I just learned that bash can do regex without external commands. Jul 18, 2022 at 11:38

With zsh only:


set -o extendedglob
printf '%s\n' ${str//(#m)????/$MATCH }


printf '%s%s%s%s ' ${(s::)str}

with ksh93 only:

printf '%s\n' "${str//????/\0 }"

With any POSIX shell only (also avoiding the trailing space if the input length is a multiple of 4):

while true; do
  case $str in
      out=$out${str%"$new_str"}' '
printf '%s\n' "$out"

Now, that's for characters. If you wanted to do it on grapheme clusters (for instance, to break Stéphane, written as $'Ste\u0301phane', as Stép hane and not Ste phan e), with zsh:

set -o rematchpcre
str=$'Ste\u301phane' out=
while [[ $str =~ '(\X{4})(.+)' ]] {
  out+="$match[1] " str=$match[2]
printf '%s\n' $out

With ksh93, you could break by display width as well, which would work for that Stéphane above, but could also help when some other sorts of zero-width or double-width characters are involved:

str=$'Ste\u301phane' out=
  start=${ printf %L.4s. "$str"; }
  [ "$start" != "$str" ]
  out+="$start " str=${str#"$start"}
printf '%s\n' "$out"

Here is the example using grep and xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
  • 2
    xargs runs echo by default, so it won't work with words like -nen or that contain backslashes depending on the echo implementation. You'll see the odd newline character once in a while as well if xargs runs more than one echo. Better to pipe to paste -sd ' ' - instead. Note that -o is not a standard option. May 5, 2017 at 15:36
  • grep -o is an interesting hack to insert newlines (potentially useful as an alternative to fold -w 4 if grep handles UTF-8 multibyte characters better), but xargs is not a robust or efficient way to convert newlines to spaces. Nov 19, 2022 at 11:45

I'm going to answer by only inserting spaces as required so a space appears at least after every 4 characters on a line; not sure which way you want to handle this case. For example, given input of "aa bbccdd", you'd get output "aa bbcc dd" rather than "aa b bccd d".

I'm using Perl for lookahead, but I'm not very familiar with Perl in general, so there may be tweaks needed:

$ echo "foobarbazblargblurg" | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
foob arba zbla rgbl urg

$ echo 'aa bbccdd' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
aa bbcc dd
# not 'aa b bccd d'!

$ echo 'some input' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
some inpu t
# not 'some  inp ut'!

$ echo $'aabb\nc cddee' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g' | 
> while read; do echo "${REPLY}x"; done
c cdde ex
# no spaces added at the end of the first line (while loop to add to the end of
# the line and show this)

I have done this by using python

First i am reading file then i am dividing by 4 characters and adding space

import re

for j in i:
print " " .join (m) + "  "

/root/l.txt==> Consists of the content which you given in example


foob arba zbla rgbl

You can also try the split command. While it is commonly used to split files, you can use it for this as well:

split -b 4 --filter='cat;echo -n " "' <<< 'foobarbazblargblurg'

I mean it works, but YMMV.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .