8

I have hundreds of *.txt files which have a common format.

I can insert a comma at a specific position in one file, how can I generalize the below code to apply this operation at several places for all *.txtfiles in the directory?

sed -i 's/^\(.\{4\}\)/\1,/' blank.txt

For example inserting commas at positions 4, 8, 22 etc.

Something like this perhaps?

for i in *.txt; do
   sed -i 's/^\(.\{4\}\)/\1,/' $i
done
3
  • Are the locations specific, or do you still need a regex to match appropriate bits?
    – thrig
    Feb 3, 2016 at 23:24
  • If, for example, you need to place a comma after the fifth character, sed s/^...../&,/ /path/to/file will do it.
    – DopeGhoti
    Feb 3, 2016 at 23:47
  • In addition to the other answers provided, your first command could be simplified from s/^\(.\{4\}\)/\1,/ to s/^.\{4\}/&,/ and still be usable as an ex command or within vi. (Of course it could be further simplified to s/./&,/4 for use in sed.)
    – Wildcard
    Feb 4, 2016 at 8:15

3 Answers 3

15

In a general way, you can just do:

sed 's/./&,/4' <in >out

That will append a comma on output to the 4th character of all input lines with at least that many characters.

And, if you'll take my advice, you should generally not use the -i switch to any sed which offers one.

3
  • Can you please explain your refrain from -i? Without it the process of changing a file is mv file file.new;sed '...' < file.new > file;rm file.new which is cumbersome and unsafe against multiple users. In place editing has the additional advantage that given a small enough file (under the system default buffer size, which is quite large for text files) the edit is atomic in respect to other users)
    – Guss
    Feb 4, 2016 at 6:24
  • 3
    @Guss - you describe it very well - though thats not the only way. -i doesn't edit the file but replaces it. It is especially a problem for multi-user systems, because, counterintuitively, the file's permissions don't matter and only it's parent directory's permissions do. -i alters metadata - maybe even ownership. The mv file.new file.old command you detail is exactly what sed does, only it doesn't advertise that fact, nor does it respect such norms as $TMPDIR nor even ~ and just writes to the read file's parent directory while it works, before mv it over the read file at exit.
    – mikeserv
    Feb 4, 2016 at 6:35
  • Yes, you are correct - I was confusing with another program. Though sed -i does retain some metadata, under (what I believe is) acceptable measures given specific conditions. For specific details, see this answer - unix.stackexchange.com/a/146054/4323 .
    – Guss
    Feb 4, 2016 at 7:45
10

Start from the rightmost one:

sed -i 's/./&,/22;s/./&,/8;s/./&,/4' ./*.txt

Otherwise, the first substitution would affect the offset for the second. You can always account for it though:

sed -i 's/./&,/4;s/./&,/9;s/./&,/24' ./*.txt
0

When you mention 'at positions 4,8 and 22', sed means 'one after positions 3,7 and 21'. You want to insert and keep the existing text as is.

This?:

sed -i -r "s/^(.{3})/\1,/;s/^(.{7})/\1,/;s/^(.{21})/\1,/" $(dirname "$0")/blank.txt

-i = --in-place aka change directly in the existing file, -r = regular expression, \1 = refers to the 'group' between parenthesis, $(dirname "$0") = to stay in the current dir

A similar sed-use when you want to e.g. set tabs instead of spaces at certain positions in a file with lines seperated by single spaces:

sed -i -r "s/^(.{10})./\1\t/;s/^(.{20})./\1\t/;s/^(.{30})./\1\t/" $(dirname "$0")/blank.txt

Substitutes the characters (in this case single spaces) at position 11, 21 and 31 by a tab. The spaces within the created columns stay untouched.

You can still get the impression that this doesn't align/display your columns well in your text-editor. The coded tabs aren't wrong, but often you need to adapt the tab-setting in the text-editor preferences. E.g. Geany -> Preferences -> Editor -> Indentation -> Tabs width 8 instead of 4.

(The latter somewhat aside your original question...)

1
  • (1) The question says s/^\(.\{4\}\)/\1,/. The fact that you say s/^(.{3})/\1,/ suggests that you’re misunderstanding the question. (2) Since the question already mentions sed -i, it seems unnecessary for you to explain what it does. (3) Why are you talking about tabs? Jul 15, 2022 at 3:09

You must log in to answer this question.

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