10

This question already has an answer here:

Using common bash tools as part of a shell script, I want to repeatedly insert a newline char ('\n') into a long string at intervals of every N chars.

For example, given this string, how would I insert a newline char every 20 chars?

head -n 50 /dev/urandom | tr -dc A-Za-z0-9

Example of the results I am trying to achieve:

ZL1WEV72TTO0S83LP2I2
MTQ8DEIU3GSSYJOI9CFE
6GEPWUPCBBHLWNA4M28D
P2DHDI1L2JQIZJL0ACFV
UDYEK7HN7HQY4E2U6VFC
RH68ZZJGMSSC5YLHO0KZ
94LMELDIN1BAXQKTNSMH
0DXLM7B5966UEFGZENLZ
4917Y741L2WRTG5ACFGQ
GRVDVT3CYOLYKNT2ZYUJ
EAVN1EY4O161VTW1P3OY
Q17T24S7S9BDG1RMKGBX
WOZSI4D35U81P68NF5SB
HH7AOYHV2TWQP27A40QC
QW5N4JDK5001EAQXF41N
FKH3Q5GOQZ54HZG2FFZS
Q89KGMQZ46YBW3GVROYH
AIBOU8NFM39RYP1XBLQM
YLG8SSIW6J6XG6UJEKXO

A use-case is to quickly make a set of random passwords or ID's of a fixed length. The way I did the above example is:

for i in {1..30}; do head /dev/random | tr -dc A-Z0-9 | head -c 20 ; echo ''; done

However, for learning purposes, I want to do it a different way. I want to start with an arbitrarily long string and insert newlines, thus breaking one string into multiple small strings of fixed char length.

marked as duplicate by Archemar, msp9011, andcoz, Jeff Schaller, Christopher Dec 19 '18 at 16:48

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

16

The venerable fold command ("written by Bill Joy on June 28, 1977") can wrap lines:

$ printf "foobarzot\n" | fold -w 3
foo
bar
zot

However, there are some edge cases

BUGS Traditional roff(7) output semantics, implemented both by GNU nroff and by mandoc(1), only uses a single backspace for backing up the previous character, even for double-width characters. The fold backspace semantics required by POSIX mishandles such backspace-encoded sequences, breaking lines early. The fmt(1) utility provides similar functionality and does not suffer from that problem, but isn't standardized by POSIX.

so if your input has backspace characters you may need to filter or remove those

$ printf "a\bc\bd\be\n" | col -b | fold -w 1
e
$ printf "a\bc\bd\be\n" | tr -d "\b" | fold -w 1
a
c
d
e
10

I like the fold answer but just in case you want with sed, here it is:

sed 's/.\{20\}/&\
/g' filename

You can use -i for in-place insertion.

  • thanks. I voted your answer up, but I like the fold solution too. – BugBuddy Dec 19 '18 at 0:48
3

If you have the contents in a variable, such as:

var=$(head -n 50 /dev/urandom | tr -dc A-Za-z0-9)

Then you could use a bash loop over the length of the variable in chunks of 20, printing out each chunk:

for((start=1;start < ${#var}; start += 20)); do printf '%s\n' "${var:start:20}"; done

If you want them as individual variables, consider assigning the output to an array:

readarray -t passwords < <(for((start=1;start < ${#var}; start += 20)); do printf '%s\n' "${var:start:20}"; done)
1

Instead of doing that tr -dc thing to get rid of non-printable chars, I would just use base64 from coreutils:

$ base64 -w20 /dev/urandom | head -8
ckXkWvb0zJknz2zi4fRS
3Jv0dDbKiX8fef7SOfbH
QJySlGUzzhi32wvrGliK
YEiuz6v+EFaRYRMjvnJq
HCXIPiP9wmgONLRqm9uK
iHYwo5xIs8gGjQQEQBeX
8NkL4EkmOAHdmWhGvZYl
AcxD2DaTq2TZRsDL+UMx

If the + and / are problem you can replace them:

$ base64 -w20 /dev/urandom | tr +/ pq | head -8
zr7MgiEr7xBd7h9ihK30
IRNvDuT2H9HsHVq9yFqh
S1cihgfAInjfFspMNXVC
qUUwGErD7nZqtzQtLOo7
DNDp4TVWvHmbEh7HLDGX
GtqqDdEoceY8m5U7FGu0
TvGtTukm6Whr7VHN1mZG
DW5TUH525IA52zLKYACV

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