0

I have a list of file names and line numbers in the following format:

./foo.txt:1:(more characters)
./foo.txt:3:(more characters)
./bar.txt:10:(more characters)

The list could be from a file or it could be output by a process (for example, grep -n).

I want to append some fixed text to each of the referenced lines, modifying the files in place.

For example, if the file foo.txt contains the following text:

One
Two
Three
Four

I want it to be modified to contain:

One TODO
Two
Three TODO
Four

Similarly, " TODO" should be appended to line 10 of bar.txt.

How can I do this?

  • 1
    Passing file names like this is tricky. If the file name contains : or new line, it will fail. – pLumo Apr 23 at 13:04
  • Well when text is coming from a stream than you could use sed as follows: ... | sed 's/\(.*\)/\1 foo/' or ... | sed 's/.*/& foo/' – Valentin Bajrami Apr 23 at 13:15
  • @ValentinBajrami it seems to me that the intention is to edit the referenced files, not the stream/input itself. – Jeff Schaller Apr 23 at 13:27
  • @ValentinBajrami: JeffSchaller is correct. I updated the question to make this clearer, it now specifically states that "the referenced lines" are to be modified. – Martin Herrmann Apr 23 at 13:30
1

Assuming your file names do contain neither : nor newline:

echo "$list_of_file_names" | while IFS=: read -r file line text; do
  sed -i "$line"'s/$/ TODO/' "$file"
done

Note: I think this is a pretty bad idea as

  • it is inefficient (runs sed for each line).
  • it will break on file names including : or \n.

Better use sed or awk for finding matching files+lines and appending text to them.

  • Good caveat about the : or newline in the file name. – Martin Herrmann Apr 24 at 9:01
  • Using sed or awk for finding and updating also seems like a good idea, provided it is feasible - which may not be the case, depending on how the list is generated. In fact, in the application that motivated the question, the list comes from an (improvised) code analysis script that is built around a grep invocation, but performs some additional processing. – Martin Herrmann Apr 24 at 9:04
3

One option: use ed!

while IFS=: read -r filename linenumber junk
do
  ed -s "$filename" <<< "$linenumber"$'s/$/ TODO/\nw\nq'
done < input
  • Does this have an advantage over using sed as per @RoVo's answer? Seems like the solutions are pretty similar, but the sed invocation looks a bit more tidy to me. – Martin Herrmann Apr 23 at 13:47
  • I wanted to put together a sed solution that batched all the edits for a single file together, to save some I/O. For example: sed -i -e 1s/$/TODO -e 3s/$/TODO ./foo.txt ... but didn't. Implementation left as an exercise for another Answerer :) – Jeff Schaller Apr 23 at 14:02

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.