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

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 '11 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 '12 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 '18 at 17:07
  • 5
    @Anubis 's/.\{4\}/& /g;s/ $//' May 23 '19 at 15:21

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 '16 at 19:18
  • 3
    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 '17 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 at 17:28

Here is the example using grep and xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
  • 1
    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 '17 at 15:36

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"

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.