0

I'm trying to edit a setup.py file with sed and this is the pattern I need to match:

from __future__ import absolute_import from __future__ import division from __future__ import print_function import fnmatch import os import re import sys from setuptools import Command from setuptools import find_packages from setuptools import setup from setuptools.command.install import install as InstallCommandBase from setuptools.dist import Distribution DOCLINES = __doc__.split('\n') _VERSION = '1.0.0' REQUIRED_PACKAGES = [ 'absl-py >= 0.7.0', 'astunparse == 1.6.3', 'backports.weakref >= 1.0rc1;python_version<"3.4"', 'scipy == 1.2.2;python_version<"3"', ] 

After matching REQUIRED_PACKAGES = [ .* ] I want to insert a line 'test == 1.1.0', before the closing ].

So final will look like this:

REQUIRED_PACKAGES = [ 'absl-py >= 0.7.0', 'astunparse == 1.6.3', 'backports.weakref >= 1.0rc1;python_version<"3.4"', 'scipy == 1.2.2;python_version<"3"', 'test == 1.1.0', ] 

I tried with Python, but looking for cleaner and simpler options.

import re SEARCH_DICT = { 'required_packages': re.compile( r'REQUIRED_PACKAGES = (?P<required_packages>.*)\n') } TEST_LIBRARY = '\t\t\'test==1.0.0\'\n' def _parse_line(line): """ Do a regex search against all defined regexes and return the key and match result of the first matching regex """ for key, rx in SEARCH_DICT.items(): match = rx.search(line) if match: return key, match # if there are no matches return None, None def parse_file(filepath): """ Parse text at given filepath Parameters ---------- filepath : str Filepath for file_object to be parsed Returns ------- data : file contents """ data = [] # create an empty list to collect the data line_index = -1 # open the file and read through it line by line with open(filepath, 'r+') as file_object: line = file_object.readline() line_index+=1 while line: # at each line check for a match with a regex key, match = _parse_line(line) if key == 'required_packages': required_packages_start = match.group('required_packages') if required_packages_start == '[': print('Found REQUIRED_PACKAGES') while line.strip(): library = line.rstrip() if library == ']': # End of required packages return line_index line = file_object.readline() line_index+=1 line = file_object.readline() line_index+=1 file_object.readline() line_index+=1 return line_index line_index = parse_file('test.test') lines = None if line_index: with open('test.test', 'r') as file_handler: lines = file_handler.readlines() lines.insert(line_index, TEST_LIBRARY) with open('test.out', 'w') as file_handler: file_handler.writelines(lines) 
0

3 Answers 3

2

sed can insert a line:

sed "/REQUIRED_PACKAGES = \[/, /]/ {/]/i\ 'test == 1.1.0', ;}" FILE 
2
  • Thanks, I get sed: 1: "/REQUIRED_PACKAGES = \[ ...": extra characters after \ at the end of i command Commented Feb 26, 2020 at 19:39
  • 1
    Sounds like a problem with copy/pasting into a shell. Try entering the first line then press <enter> and enter the second line Commented Feb 26, 2020 at 22:39
1

sed is a line-oriented tool. I believe better would be to use awk, it will more easily detect that block to which you want and append a line. There may be a more elegant way, but this does what you want:

awk '$0=="REQUIRED_PACKAGES = [" {found=1} found==1 && $0=="]" {print " \047test == 1.1.0\047"; found=0} {print}' setup.py 

Quite simple to understand: It prints every line of the file, but before printing the closing bracket of the REQUIRED_PACKAGES block it prints the line you want to append.

0
1
$ cat file _VERSION = '1.0.0' REQUIRED_PACKAGES = [ 'absl-py >= 0.7.0', 'astunparse == 1.6.3', 'backports.weakref >= 1.0rc1;python_version<"3.4"', 'scipy == 1.2.2;python_version<"3"', ] 

.

$ cat tst.awk /REQUIRED_PACKAGES = \[/ { inBlock=1 } inBlock { if ( /]/ ) { sub(/[^[:space:]].*/,"",prev) print prev "\047test == 1.1.0\047," inBlock = prev = "" } prev = $0 } { print } 

.

$ awk -f tst.awk file _VERSION = '1.0.0' REQUIRED_PACKAGES = [ 'absl-py >= 0.7.0', 'astunparse == 1.6.3', 'backports.weakref >= 1.0rc1;python_version<"3.4"', 'scipy == 1.2.2;python_version<"3"', 'test == 1.1.0', ] 

Note that the above inserts the new text using whatever indentation the previous line was using.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.