How to replace first line of every file using awk

How to replace first line of every file with mm200 ? Thanx in advance.

file.mail

>mm89589585989��*
GGG
>HH
DG

file1.mail

>mm454695879357
dg

output

filemail

>mm200
GGG
>HH
DG

file1.mail

>mm200
dg

Try : for every first line

$ cat <<eof | awk '$0 = NR==1 ? replace : $0' replace=">mm200"
>mm89589585989��*
GGG
>HH
DG
eof

>mm200
GGG
>HH
DG

for file use like this

$ awk '$0 = NR==1 ? replace : $0' replace=">mm200" file

for many files..

for file in *.type; do
       awk '$0 = NR==1 ? replace : $0' replace=">mm200" $file >tmpfile && mv tmpfile $file
done
1 Like

Assuming that all the files are in the same directory :

#!/bin/sh

for file in /path/to/files/*
do
  if [ -f "$file" ]
  then
    sed '1 s/.*/mm200/' $file >tmpFile
    cp tmpFile $file
    rm tmpfFile
  fi
done

@Akshay

for many file this is working fine like this

awk '$0 = NR==1 ? replace : $0' replace=">mm200" file*

for many files this won't work because awk will not understand new file with file* as input, since we are not even checking whether FNR is resetting, so for many files copy last code in post #1 that will work.

--edit--

change mv tmpfile $file to mv -f tmpfile $file once your test gets over with sample data, and Sorry I forgot to put this note in post #1

1 Like

@Akshay Hegde: This is working fine, but it replaces every single line in the file by itself or by replace's contents, and - admittedly remote possibility - should there be an empty or zero-valued line, it would be suppressed. Why not simply use

awk 'NR==1 {$0=replace} 1' replace=">mm200" file
1 Like

Thanks RudiC. Yes what you said is true.. I just tested like this...thanks for bringing this to my notice.

$ awk 'BEGIN{for(i=1;i<=5;i++)print i;print}' | awk '$0 = NR==1 ? replace : $0' replace=">mm200"
>mm200
2
3
4
5
$ awk 'BEGIN{for(i=1;i<=5;i++)print i;print}' | awk 'NR==1 {$0=replace} 1' replace=">mm200"
>mm200
2
3
4
5
            --- > empty line

then the final version is like this ?

for file in *.type; do
       awk 'NR==1 {$0=replace} 1' replace=">mm200" $file >tmpfile && mv -f tmpfile $file
done

@quincyjones Yes final version is like that. as long as there is no empty line what I posted will work, if there is any empty line my solution suppresses it (removes empty line).

Not bad but I would modify it a little to make it safer:

# VITAL:  Back up first!  Otherwise one typo could mangle all your input files
# and make you very sad.
tar -cf type-backup.tar *.type

for file in *.type; do
       awk 'NR==1 {$0=replace} 1' replace=">mm200" "$file" > tmpfile
       # Using cat instead of mv means $file will not change ownership
       # or permissions by accident.  This is because it overwrites the file,
       # rather than deleting it and putting a brand-new file in its place.
       cat tmpfile > "$file"
done

rm -f tmpfile

Alternatively you can overwrite the file in awk like this (test first, and correct me if I'm wrong):

awk '{if(NR==1)$0=replace; print > FILENAME}' replace=">mm200" *.type

Sorry, that is incorrect. Doing that will truncate the file before awk is finished reading it, throwing away most of its contents.

Even things like sed -i actually use temporary files to store changes to the file until they're ready to replace it.

1 Like

You're right, it only works on small files, thanks :b:

That it even works on small files means awk must be reading several lines in advance for you. You trash the original file pretty instantly, but it won't realize until it runs out of data and needs to read again.

How about this ?

Generate some files for test

$ awk 'BEGIN{for(i=1;i<=5;i++)for(j=1;j<=5;j++)print j >"file"i".tmp"}'
$ ls file*.tmp -1
file1.tmp
file2.tmp
file3.tmp
file4.tmp
file5.tmp

BEFORE

$ awk '{ print FNR==1 && NR!=1 || NR==1 ? $0 FS FILENAME : $0 }' file*.tmp
1 file1.tmp
2
3
4
5
1 file2.tmp
2
3
4
5
1 file3.tmp
2
3
4
5
1 file4.tmp
2
3
4
5
1 file5.tmp
2
3
4
5
awk '

function write_2_og_file(file){
                                 system(sprintf("%s %s %s","cat","tmpfile>",file))
                              }

     NR==1 || NR!=1 && FNR==1{
                                  w  = "tmpfile"
                                  close(w)
                                  $0 = replace
                                  f = FILENAME
                             }
              NR!=1 && FNR==1{
                                  write_2_og_file(p)
                             }
                             {
                                  print $0 >w
                                  p=f
                             }
                          END{
                                  close(w)
                                  write_2_og_file(p)
                                  system("rm tmpfile")
                             }
    '   replace="<m200" file*.tmp

AFTER

$ awk '{ print FNR==1 && NR!=1 || NR==1 ? $0 FS FILENAME : $0 }' file*.tmp
<m200 file1.tmp
2
3
4
5
<m200 file2.tmp
2
3
4
5
<m200 file3.tmp
2
3
4
5
<m200 file4.tmp
2
3
4
5
<m200 file5.tmp
2
3
4
5

Let me know if I missed anything...

I realize that the title of this thread requests a solution in awk, but most of the discussion in this thread seems to revolve around the fact that awk is not the correct tool for this job. Using ed or ex as in:

#!/bin/ksh
for i in "$@"
do      ed -s "$i" <<EOF
                1s/.*/>mm200/
                w
                q
EOF
done

avoids issues with input files being truncated, permissions changing, ownership changing, etc.