Score:2

Replace a line of text in a file, with the contents of another file

cn flag

I have two files, let's call them foo.py and fixed_rule_map. I need to:

  • find the line in foo.py that contains the pattern rule_map = . It would be enough to find the pattern rule_map, if that's easier, but since there are more than one line in foo.py which contains rule_map, in this case I need to match only the first occurrence.
  • substitute the line in foo.py with the contents of fix_rule_map_in_foo (which contains a single line of text).

For example, foo.py may look like this:

class example:
    """some useless text.
    
    """
    rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y}

    def __init__(self,
                rule=None,
                ):
        self._init_rule(rule)


    def _init_rule(self, rule):
        if rule not in self.rule_map and rule is not None:
            raise KeyError(f'rule must be greater, less or None, '
                           f'but got {rule}.')
        self.rule = rule
        if self.rule is not None:
            self.compare_func = self.rule_map[self.rule]

and I would like to correct it to this:

class example:
    """some useless text.
    
    """
    rule_map = {'greater': lambda x, y: (x if x is not None else -inf) > (y if y is not None else -inf), 'less': lambda x, y: (x if x is not None else -inf) < (y if y is not None else -inf)}

    def __init__(self,
                rule=None,
                ):
        self._init_rule(rule)


    def _init_rule(self, rule):
        if rule not in self.rule_map and rule is not None:
            raise KeyError(f'rule must be greater, less or None, '
                           f'but got {rule}.')
        self.rule = rule
        if self.rule is not None:
            self.compare_func = self.rule_map[self.rule]

thus fixed_rule_map would be a text file containing

    rule_map = {'greater': lambda x, y: (x if x is not None else -inf) > (y if y is not None else -inf), 'less': lambda x, y: (x if x is not None else -inf) < (y if y is not None else -inf)}

Note that since foo.py is a Python file, the substitution must preserve indentation.

The command that performs the substitution has to go in a Dockerfile, since the substitution must be performed when building the Docker image. Docker uses /bin/sh as an interpreter for RUN commands, rather than /bin/bash, which may be a problem if your solution uses bash. However, I think I could fix this by using a shell script fix_rule_map_in_foo.sh:

#! /bin/bash
FOO_PATH = /path/to/foo
<your_solution>

and adding this line in the Dockerfile:

RUN fix_rule_map_in_foo.sh
Raffa avatar
jp flag
Something like this `awk -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py`
cn flag
@Raffa sounds interesting. Can you write it as an answer and explain in detail the syntax of the command?
Score:3
hr flag

You can use the sed editor's r command to read the contents of a file and insert them, deleting the original matching line after:

sed -e '/rule_map = /{r fix_rule_map_in_foo' -e 'd;}' foo.py

The new contents will be written to standard output by default, but GNU sed supports an in-place editing flag (--in-place or -i).

Since this doesn't involve any shell constructs, I think you can run it directly (although I don't use docker, so don't know for sure):

RUN sed -i.bak -e '/rule_map = /{r fix_rule_map_in_foo' -e 'd;}' foo.py

however you can put it in a POSIX shell script if you prefer.

Alternatively, using the non-stream editor ed (this does use shell constructs, so must be in a script):

#!/bin/sh

printf '%s\n' '/rule_map = /r fix_rule_map_in_foo' '-1d' 'wq' | ed -s foo.py
cn flag
why `sed -i.bak`? Shoudn't the command be `sed -i`?
cn flag
it looks like `sed` in my Docker image supports the `-i` option, so this should work.
Score:2
jp flag

You can set that single line contained in the file fix_rule_map_in_foo in a variable named e.g. fix with command substitution like so:

fix="$(cat fix_rule_map_in_foo)"

You can set the variable to a fixed string as well if you wish (instead of reading that string from a file) like so:

fix="The substitution string here"

Then pas that variable to awk so you can match the line and substitute it like so:

awk -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py

That will search for rule_map = and replace the line that contains the match with the value of the variable fix and print all the lines(after modification) to the terminal ... you can redirect the output to a file if you wish with e.g. >.

If you wish to edit the file foo.py instead, then use the GNU awk like so:

gawk -i inplace -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py 
cn flag
The Docker image I'm using doesn't have `gawk`, but only `awk`. Isn't it possible to edit the file `foo.py` using `awk`?
cn flag
Also, I made some edits to the question. Could you please have a look? Thanks. It's important that the substitution preserves the indentation, since `foo.py` is a Python script and indentation matters.
Raffa avatar
jp flag
@DeltaIV Not natively but you can ether use command substitution like `echo $(awk -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py) > foo.py` or ...
Raffa avatar
jp flag
Write to a temp file then rename it overwriting the original file `awk -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py > tmpfile && mv tmpfile foo.py` ...
Raffa avatar
jp flag
That said some versions of `awk` do that so test first `awk -i inplace -v fix="$(cat fix_rule_map_in_foo)" '/rule_map = / {$0 = fix}1' foo.py` ... might work.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.