2

Okay, looking for a quick way to do this. I have a list of line numbers that need to be changed in a text data file. The 16 bit pattern on that line can be anything but I need to change to change it to read XXXXXXXXXXXXXXXX based solely on the line number. Again I have a long list of line numbers I know that need to be changed. There is no discernible pattern to the lines that need to be changed. (I did not write the data pattern but I know what lines need to be changed to read all X's.) I read through a lot of the answers here and none really quite deal with this.

If this isn't clear I'll put it another way...

Change lines 26115, 32198, 37256, 40001, 40023 in a file to read XXXXXXXXXXXXXXX. I have over 100,000 lines that need changing in a 1.9 million line file.

3

As an extension of @Gilles's answer, since you say you have the line numbers that need changed in a file (which I will assume is sorted and called linums)

awk '
  BEGIN { getline NEXT < "linums" }
  NR == NEXT { $0 = "XXXXXXXXXXXXXXX"; getline NEXT < "linums" }
  1
'

This scales well to changing several thousand lines without having to manually type these several thousand line numbers.

Alternatively, with a slight modification, you can take either the line numbers or the file to be changed on stdin. I'd make a script for this (I've called it redact.awk)

#!/usr/bin/awk -f
BEGIN {
    LINUMS = ARGV[1]
    ARGV[1] = ARGV[2]
    --ARGC
    getline NEXT < LINUMS
}
NR == NEXT {
    $0 = "XXXXXXXXXXXXXXX"
    getline NEXT < LINUMS
}
1

Then you can use any of:

$ ./redact.awk linums file-to-be-changed
$ ./redact.awk - file-to-be-changed
$ ./redact.awk linums -
$ ./redact.awk linums

(The last two are equivalent)

2

Either sed or awk works well for this task.

sed '
    26115 s/.*/XXXXXXXXXXXXXXX/
    32198 s/.*/XXXXXXXXXXXXXXX/
    37256 s/.*/XXXXXXXXXXXXXXX/
    40001 s/.*/XXXXXXXXXXXXXXX/
    40023 s/.*/XXXXXXXXXXXXXXX/
'
awk '
  NR==26115 || NR==32198 || NR==37256 || NR==40001 || NR==40023 {$0 = "XXXXXXXXXXXXXXX"}
  1
'

(The lone 1 prints all lines, after the possible transformation performed by the preceding code.)

1
  • I think I'll go with the sed solution as I already have the line numbers in a file. Thanks for the quick response. The awk solution would be a bit too much typing. :) May 8 '17 at 1:41
2
sed -e '1{x;s/^/XXXXXXXXXXXXXXX/;x;}
   26115bp
   32198bp
   37256bp
   40001bp
   40023bp
   d
   :p
   g
' data_file

We first stuff the hold space with the desired pattern XXXXXXXXX and then recall that only for the desired line numbers by jumping to the label :p which shall retrieve hold space which will then implicitly get carried over to stdout. The non-matching lines are deleted (change the d to b if you want to keep them).

1

Since the replacement is static and since the operation of replacing multiple lines is so simple in sed, one could possibly create one big sed script to do the work.

Assuming that you have the line numbers in a separate file, linenos.txt, one line number per line, then we can produce the (GNU) sed script through

$ awk '{ printf("%dc XXXXXXXXXXXXXXX\n", $0) }' linenos.txt >script.sed

or

$ awk '{ print $0, "c XXXXXXXXXXXXXXX" }' linenos.txt >script.sed

Then, it's a matter of applying it to a file:

$ sed -f script.sed file >file.new

Note: I have never run an exceedingly large sed script, so I don't know how GNU sed handles it performance-wise.

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.