|repostatus| |ci-status| |coverage| |pyversions| |license|
.. |repostatus| image:: https://www.repostatus.org/badges/latest/active.svg
:target: https://www.repostatus.org/#active
:alt: Project Status: Active — The project has reached a stable, usable
state and is being actively developed.
.. |ci-status| image:: https://github.com/jwodder/lineinfile/actions/workflows/test.yml/badge.svg
:target: https://github.com/jwodder/lineinfile/actions/workflows/test.yml
:alt: CI Status
.. |coverage| image:: https://codecov.io/gh/jwodder/lineinfile/branch/master/graph/badge.svg
:target: https://codecov.io/gh/jwodder/lineinfile
.. |pyversions| image:: https://img.shields.io/pypi/pyversions/lineinfile.svg
:target: https://pypi.org/project/lineinfile/
.. |license| image:: https://img.shields.io/github/license/jwodder/lineinfile.svg
:target: https://opensource.org/licenses/MIT
:alt: MIT License
`GitHub <https://github.com/jwodder/lineinfile>`_
| `PyPI <https://pypi.org/project/lineinfile/>`_
| `Issues <https://github.com/jwodder/lineinfile/issues>`_
| `Changelog <https://github.com/jwodder/lineinfile/blob/master/CHANGELOG.md>`_
Inspired by (but not affiliated with) `the Ansible module of the same name`__,
``lineinfile`` provides a command and library for adding a line to a file if
it's not already there and for removing lines matching a pattern from a file.
There are options for using a regex to find a line to update or to determine
which line to insert before or after. There are options for backing up the
modified file with a custom file extension and for treating a nonexistent file
as though it's just empty. There's even an option for determining the line to
insert based on capturing groups in the matching regex.
__ https://docs.ansible.com/ansible/latest/collections/ansible/builtin/
lineinfile_module.html
Unlike the Ansible module, this package does not perform any management of file
attributes; those must be set externally.
Installation
============
``lineinfile`` requires Python 3.8 or higher. Just use `pip
<https://pip.pypa.io>`_ for Python 3 (You have pip, right?) to install
``lineinfile`` and its dependencies::
python3 -m pip install lineinfile
Examples
========
A crude ``.ini``-file updater: Set ``theoption`` to ``value``, and if no
setting for ``theoption`` is found in the file, add one after the line
"``[thesection]``":
.. code:: console
$ lineinfile add \
--after-first "^\[thesection\]$" \
-e "^theoption\s*=" \
"theoption = thevalue" \
settings.ini
The equivalent operation in Python:
.. code:: python
from lineinfile import AfterFirst, add_line_to_file
add_line_to_file(
"settings.ini",
"theoption = thevalue",
regexp=r"^theoption\s*=",
inserter=AfterFirst(r"^\[thesection\]$"),
)
Replace the first instance of "``foo = ...``" with "``foo = 'bar'``",
preserving indentation, and create a backup of the file with the extension
``.bak``, even if no changes were made:
.. code:: console
$ lineinfile add \
-e "^(\s*)foo\s*=" \
--backrefs \
--match-first \
--backup-always -i.bak \
"\1foo = 'bar'" \
file.py
The equivalent operation in Python:
.. code:: python
from lineinfile import ALWAYS, add_line_to_file
add_line_to_file(
"file.py",
r"\1foo = 'bar'",
regexp=r"^(\s*)foo\s*=",
backrefs=True,
match_first=True,
backup=ALWAYS,
backup_ext=".bak",
)
Command-Line Usage
==================
The ``lineinfile`` command has two subcommands, ``add`` and ``remove``.
``add``
-------
::
lineinfile add [<options>] <line> [<file>]
lineinfile add [<options>] -L <line> [<file>]
Add the given ``line`` (after expanding backslash escapes) to the file if it is
not already present. If a `Python regular expression`_ is given with the
``-e``/``--regexp`` option and it matches any lines in the file, ``line`` will
replace the last matching line (or the first matching line, if
``--match-first`` is given). If the regular expression does not match any
lines (or no regular expression is specified) and ``line`` is not found in the
file, the line is inserted at the end of the file by default; this can be
changed with the ``--after-first``, ``--after-last``, ``--before-first``,
``--before-last``, and ``--bof`` options.
If no file name is given on the command line, input is read from standard
input, and the result is written to standard output. It is an error to specify
any of the ``--backup-changed``, ``--backup-always``, ``--backup-ext``, or
``--create`` options when no file is given.
.. _Python regular expression: https://docs.python.org/3/library/re.html
#regular-expression-syntax
Options
```````
-a REGEX, --after-first REGEX
If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` after the first line that
matches the regular expression ``REGEX``, or at the
end of the file if no line matches ``REGEX``.
-A REGEX, --after-last REGEX
If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` after the last line that
matches the regular expression ``REGEX``, or at the
end of the file if no line matches ``REGEX``.
-b REGEX, --before-first REGEX
If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` before the first line
that matches the regular expression ``REGEX``, or
at the end of the file if no line matches
``REGEX``.
-B REGEX, --before-last REGEX
If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` before the last line that
matches the regular expression ``REGEX``, or at the
end of the file if no line matches ``REGEX``.
--bof If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` at the beginning of the
file.
--eof If neither ``line`` nor ``--regexp`` is found in
the file, insert ``line`` at the end of the file.
This is the default.
-e REGEX, --regexp REGEX If the given regular expression matches any lines
in the file, replace the last matching line (or
first, if ``--match-first`` is given) with
``line``.
--backrefs If ``--regexp`` matches, the capturing groups in
the regular expression are used to expand any
``\n``, ``\g<n>``, or ``\g<name>`` backreferences
in ``line``, and the resulting string replaces the
matched line in the input.
If ``--regexp`` does not match, the input is left
unchanged.
It is an error to specify this option without
``--regexp``.
--backup, --backup-changed If the input file is modified, create a backup of
the original file. The backup will have the
extension specified with ``--backup-ext`` (or ``~``
if no extension is specified) appended to its
filename.
--backup-always Create a backup of the original file regardless of
whether or not it's modified. The backup will have
the extension specified with ``--backup-ext`` (or
``~`` if no extension is specified) appended to its
filename.
-i EXT, --backup-ext EXT Create a backup of the input file with ``EXT``
added to the end of the filename. Implies
``--backup-changed`` if neither it nor
``--backup-always`` is also given.
-c, --create If the input file does not exist, pretend it's
empty instead of erroring, and create it with the
results of the operation. No backup file will be
created for a nonexistent file, regardless of the
other options.
If the input file does not exist and no changes are
made (because ``--backrefs`` was specified and
``--regexp`` didn't match), the file will not be
created.
-L LINE, --line LINE Use ``LINE`` as the line to insert. This option is
useful when ``LINE`` begins with a hyphen.
-m, --match-first If ``--regexp`` matches, replace the first matching
line with ``line``.
-M, --match-last If ``--regexp`` matches, replace the last matching
line with ``line``. This is the default.
-o FILE, --outfile FILE Write the resulting file contents to ``FILE``
instead of modifying the input file.
It is an error to specify this option with any of
``--backup-changed``, ``--backup-always``, or
``--backup-ext``.
``remove``
----------
::
lineinfile remove [<options>] <regexp> [<file>]
lineinfile remove [<options>] -e <regexp> [<file>]
Delete all lines from the given file that match the given `Python regular
expression`_.
If no file name is given on the command line, input is read from standard
input, and the result is written to standard output. It is an error to specify
any of the ``--backup-changed``, ``--backup-always``, or ``--backup-ext``
options when no file is given.
Options
```````
--backup, --backup-changed If the input file is modified, create a backup of
the original file. The backup will have the
extension specified with ``--backup-ext`` (or ``~``
if no extension is specified) appended to its
filename.
--backup-always Create a backup of the original file regardless of
whether or not it's modified. The backup will have
the extension specified with ``--backup-ext`` (or
``~`` if no extension is specified) appended to its
filename.
-i EXT, --backup-ext EXT Create a backup of the input file with ``EXT``
added to the end of the filename. Implies
``--backup-changed`` if neither it nor
``--backup-always`` is also given.
-e REGEX, --regexp REGEX Delete all lines that match ``REGEX``. This option
is useful when ``REGEX`` begins with a hyphen.
-o FILE, --outfile FILE Write the resulting file contents to ``FILE``
instead of modifying the input file.
It is an error to specify this option with any of
``--backup-changed``, ``--backup-always``, or
``--backup-ext``.
Library API
===========
Note that all regular expression matching is done with the ``Pattern.search()``
method, i.e., it is not anchored at the start of the line. In order to force a
regular expression to start matching at the beginning of a line, prefix it with
``^`` or ``\A``.
.. code:: python
lineinfile.add_line_to_file(
filepath: Union[str, bytes, os.PathLike[str], os.PathLike[bytes]],
line: str,
regexp: Optional[Union[str, re.Pattern[str]]] = None,
inserter: Optional[Inserter] = None,
match_first: bool = False,
backrefs: bool = False,
backup: Optional[BackupWhen] = None,
backup_ext: Optional[str] = None,
create: bool = False,
encoding: Optional[str] = None,
errors: Optional[str] = None,
) -> bool
Add the given ``line`` to the file at ``filepath`` if it is not already
present. Returns ``True`` if the file is modified. If ``regexp`` is set to a
regular expression (either a string or a compiled pattern object) and it
matches any lines in the file, ``line`` will replace the last matching line (or
the first matching line, if ``match_first=True``). If the regular expression
does not match any lines (or no regular expression is specified) and ``line``
is not found in the file, the line is inserted at the end of the file by
default; this can be changed by passing the appropriate object as the
``inserter`` argument; see "Inserters_" below.
When ``backrefs`` is true, if ``regexp`` matches, the capturing groups in the
regular expression are used to expand any ``\n``, ``\g<n>``, or ``\g<name>``
backreferences in ``line``, and the resulting string replaces the matched line
in the input. If ``backrefs`` is true and ``regexp`` does not match, the file
is left unchanged. It is an error to set ``backrefs`` to true without also
setting ``regexp``.
When ``backup`` is set to ``lineinfile.CHANGED``, a backup of the file's
original contents is created if the file is modified. When ``backup`` is set
to ``lineinfile.ALWAYS``, a backup is always created, regardless of whether the
file is modified. The name of the backup file will be the same as the
original, with the value of ``backup_ext`` (default: ``~``) appended.
If ``create`` is true and ``filepath`` does not exist, pretend it's empty
instead of erroring, and create it with the results of the operation. No
backup file will ever be created for a nonexistent file. If ``filepath`` does
not exist and no changes are made (because ``backrefs`` was set and ``regexp``
didn't match), the file will not be created.
.. code:: python
lineinfile.remove_lines_from_file(
filepath: Union[str, bytes, os.PathLike[str], os.PathLike[bytes]],
regexp: Union[str, re.Pattern[str]],
backup: Optional[BackupWhen] = None,
backup_ext: Optional[str] = None,
encoding: Optional[str] = None,
errors: Optional[str] = None,
) -> bool
Delete all lines from the file at ``filepath`` that match the regular
expression ``regexp`` (either a string or a compiled pattern object). Returns
``True`` if the file is modified.
When ``backup`` is set to ``lineinfile.CHANGED``, a backup of the file's
original contents is created if the file is modified. When ``backup`` is set
to ``lineinfile.ALWAYS``, a backup is always created, regardless of whether the
file is modified. The name of the backup file will be the same as the
original, with the value of ``backup_ext`` (default: ``~``) appended.
.. code:: python
lineinfile.add_line_to_string(
s: str,
line: str,
regexp: Optional[Union[str, re.Pattern[str]]] = None,
inserter: Optional[Inserter] = None,
match_first: bool = False,
backrefs: bool = False,
) -> str
Add the given ``line`` to the string ``s`` if it is not already present and
return the result. If ``regexp`` is set to a regular expression (either a
string or a compiled pattern object) and it matches any lines in the input,
``line`` will replace the last matching line (or the first matching line, if
``match_first=True``). If the regular expression does not match any lines (or
no regular expression is specified) and ``line`` is not found in the input, the
line is inserted at the end of the input by default; this can be changed by
passing the appropriate object as the ``inserter`` argument; see "Inserters_"
below.
When ``backrefs`` is true, if ``regexp`` matches, the capturing groups in the
regular expression are used to expand any ``\n``, ``\g<n>``, or ``\g<name>``
backreferences in ``line``, and the resulting string replaces the matched line
in the input. If ``backrefs`` is true and ``regexp`` does not match, the input
is left unchanged. It is an error to set ``backrefs`` to true without also
setting ``regexp``.
.. code:: python
lineinfile.remove_lines_from_string(
s: str,
regexp: Union[str, re.Pattern[str]],
) -> str
Delete all lines from the string ``s`` that match the regular expression
``regexp`` (either a string or a compiled pattern object) and return the
result.
Inserters
---------
Inserters are objects used by the ``add_line_*`` functions to determine the
location at which to insert ``line`` when it is not found in the input and the
``regexp`` argument, if set, doesn't match any lines.
``lineinfile`` provides the following inserter classes:
``AtBOF()``
Always inserts the line at the beginning of the file
``AtEOF()``
Always inserts the line at the end of the file
``AfterFirst(regexp)``
Inserts the line after the first input line that matches the given regular
expression (either a string or a compiled pattern object), or at the end of
the file if no line matches.
``AfterLast(regexp)``
Inserts the line after the last input line that matches the given regular
expression (either a string or a compiled pattern object), or at the end of
the file if no line matches.
``BeforeFirst(regexp)``
Inserts the line before the first input line that matches the given regular
expression (either a string or a compiled pattern object), or at the end of
the file if no line matches.
``BeforeLast(regexp)``
Inserts the line before the last input line that matches the given regular
expression (either a string or a compiled pattern object), or at the end of
the file if no line matches.
Handling of Line Endings
========================
``lineinfile`` operates on files using Python's universal newlines mode, in
which all LF (``\n``), CR LF (``\r\n``), and CR (``\r``) sequences in a file
are converted to just LF when read into a Python string, and LF is in turn
converted to the operating system's native line separator when written back to
disk.
In the majority of cases, this allows you to use ``$`` in regular expressions
and have it always match the end of an input line, regardless of what line
ending the line had on disk. However, when using ``add_line_to_string()`` or
``remove_lines_from_string()`` with a string with non-LF line separators,
things can get tricky. ``lineinfile`` follows the following rules regarding
line separators:
- Lines are terminated by LF, CR, and CR LF only.
- When an ``add_line_*`` function compares a ``line`` argument against a line
in the input, the line ending is stripped from both lines. This is a
deviation from Ansible's behavior, where only the input line is stripped.
- When matching an input line against ``regexp`` or an inserter, line endings
are not stripped. Note that a regex like ``r"foo$"`` will not match a line
that ends with a non-LF line ending, so this can result in patterns not
matching where you might naïvely expect them to match.
- When adding a line to the end of a file, if the file does not end with a line
ending already, an LF is appended before adding the line.
- When adding ``line`` to a document (either as a new line or replacing a
pre-existing line), LF is appended to the line if it does not already end
with a line separator; any line ending on the line being replaced (if any) is
ignored (If you want to preserve it, use backrefs). If the only difference
between the resulting ``line`` and the line it's replacing is the line
ending, the replacement still occurs, the line ending is modified, and the
document is changed.
Raw data
{
"_id": null,
"home_page": null,
"name": "lineinfile",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "add line, edit, ensure line, insert line, regexp, remove line, sed",
"author": null,
"author_email": "John Thorvald Wodder II <lineinfile@varonathe.org>",
"download_url": "https://files.pythonhosted.org/packages/22/cc/519301c8f0beba6dfd86b32f624c6fa935cab352aa960cdabc3460e02284/lineinfile-0.4.2.tar.gz",
"platform": null,
"description": "|repostatus| |ci-status| |coverage| |pyversions| |license|\n\n.. |repostatus| image:: https://www.repostatus.org/badges/latest/active.svg\n :target: https://www.repostatus.org/#active\n :alt: Project Status: Active \u2014 The project has reached a stable, usable\n state and is being actively developed.\n\n.. |ci-status| image:: https://github.com/jwodder/lineinfile/actions/workflows/test.yml/badge.svg\n :target: https://github.com/jwodder/lineinfile/actions/workflows/test.yml\n :alt: CI Status\n\n.. |coverage| image:: https://codecov.io/gh/jwodder/lineinfile/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/jwodder/lineinfile\n\n.. |pyversions| image:: https://img.shields.io/pypi/pyversions/lineinfile.svg\n :target: https://pypi.org/project/lineinfile/\n\n.. |license| image:: https://img.shields.io/github/license/jwodder/lineinfile.svg\n :target: https://opensource.org/licenses/MIT\n :alt: MIT License\n\n`GitHub <https://github.com/jwodder/lineinfile>`_\n| `PyPI <https://pypi.org/project/lineinfile/>`_\n| `Issues <https://github.com/jwodder/lineinfile/issues>`_\n| `Changelog <https://github.com/jwodder/lineinfile/blob/master/CHANGELOG.md>`_\n\nInspired by (but not affiliated with) `the Ansible module of the same name`__,\n``lineinfile`` provides a command and library for adding a line to a file if\nit's not already there and for removing lines matching a pattern from a file.\nThere are options for using a regex to find a line to update or to determine\nwhich line to insert before or after. There are options for backing up the\nmodified file with a custom file extension and for treating a nonexistent file\nas though it's just empty. There's even an option for determining the line to\ninsert based on capturing groups in the matching regex.\n\n__ https://docs.ansible.com/ansible/latest/collections/ansible/builtin/\n lineinfile_module.html\n\nUnlike the Ansible module, this package does not perform any management of file\nattributes; those must be set externally.\n\n\nInstallation\n============\n``lineinfile`` requires Python 3.8 or higher. Just use `pip\n<https://pip.pypa.io>`_ for Python 3 (You have pip, right?) to install\n``lineinfile`` and its dependencies::\n\n python3 -m pip install lineinfile\n\n\nExamples\n========\n\nA crude ``.ini``-file updater: Set ``theoption`` to ``value``, and if no\nsetting for ``theoption`` is found in the file, add one after the line\n\"``[thesection]``\":\n\n.. code:: console\n\n $ lineinfile add \\\n --after-first \"^\\[thesection\\]$\" \\\n -e \"^theoption\\s*=\" \\\n \"theoption = thevalue\" \\\n settings.ini\n\nThe equivalent operation in Python:\n\n.. code:: python\n\n from lineinfile import AfterFirst, add_line_to_file\n\n add_line_to_file(\n \"settings.ini\",\n \"theoption = thevalue\",\n regexp=r\"^theoption\\s*=\",\n inserter=AfterFirst(r\"^\\[thesection\\]$\"),\n )\n\nReplace the first instance of \"``foo = ...``\" with \"``foo = 'bar'``\",\npreserving indentation, and create a backup of the file with the extension\n``.bak``, even if no changes were made:\n\n.. code:: console\n\n $ lineinfile add \\\n -e \"^(\\s*)foo\\s*=\" \\\n --backrefs \\\n --match-first \\\n --backup-always -i.bak \\\n \"\\1foo = 'bar'\" \\\n file.py\n\nThe equivalent operation in Python:\n\n.. code:: python\n\n from lineinfile import ALWAYS, add_line_to_file\n\n add_line_to_file(\n \"file.py\",\n r\"\\1foo = 'bar'\",\n regexp=r\"^(\\s*)foo\\s*=\",\n backrefs=True,\n match_first=True,\n backup=ALWAYS,\n backup_ext=\".bak\",\n )\n\n\nCommand-Line Usage\n==================\n\nThe ``lineinfile`` command has two subcommands, ``add`` and ``remove``.\n\n``add``\n-------\n\n::\n\n lineinfile add [<options>] <line> [<file>]\n lineinfile add [<options>] -L <line> [<file>]\n\nAdd the given ``line`` (after expanding backslash escapes) to the file if it is\nnot already present. If a `Python regular expression`_ is given with the\n``-e``/``--regexp`` option and it matches any lines in the file, ``line`` will\nreplace the last matching line (or the first matching line, if\n``--match-first`` is given). If the regular expression does not match any\nlines (or no regular expression is specified) and ``line`` is not found in the\nfile, the line is inserted at the end of the file by default; this can be\nchanged with the ``--after-first``, ``--after-last``, ``--before-first``,\n``--before-last``, and ``--bof`` options.\n\nIf no file name is given on the command line, input is read from standard\ninput, and the result is written to standard output. It is an error to specify\nany of the ``--backup-changed``, ``--backup-always``, ``--backup-ext``, or\n``--create`` options when no file is given.\n\n.. _Python regular expression: https://docs.python.org/3/library/re.html\n #regular-expression-syntax\n\nOptions\n```````\n\n-a REGEX, --after-first REGEX\n If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` after the first line that\n matches the regular expression ``REGEX``, or at the\n end of the file if no line matches ``REGEX``.\n\n-A REGEX, --after-last REGEX\n If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` after the last line that\n matches the regular expression ``REGEX``, or at the\n end of the file if no line matches ``REGEX``.\n\n-b REGEX, --before-first REGEX\n If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` before the first line\n that matches the regular expression ``REGEX``, or\n at the end of the file if no line matches\n ``REGEX``.\n\n-B REGEX, --before-last REGEX\n If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` before the last line that\n matches the regular expression ``REGEX``, or at the\n end of the file if no line matches ``REGEX``.\n\n--bof If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` at the beginning of the\n file.\n\n--eof If neither ``line`` nor ``--regexp`` is found in\n the file, insert ``line`` at the end of the file.\n This is the default.\n\n-e REGEX, --regexp REGEX If the given regular expression matches any lines\n in the file, replace the last matching line (or\n first, if ``--match-first`` is given) with\n ``line``.\n\n--backrefs If ``--regexp`` matches, the capturing groups in\n the regular expression are used to expand any\n ``\\n``, ``\\g<n>``, or ``\\g<name>`` backreferences\n in ``line``, and the resulting string replaces the\n matched line in the input.\n\n If ``--regexp`` does not match, the input is left\n unchanged.\n\n It is an error to specify this option without\n ``--regexp``.\n\n--backup, --backup-changed If the input file is modified, create a backup of\n the original file. The backup will have the\n extension specified with ``--backup-ext`` (or ``~``\n if no extension is specified) appended to its\n filename.\n\n--backup-always Create a backup of the original file regardless of\n whether or not it's modified. The backup will have\n the extension specified with ``--backup-ext`` (or\n ``~`` if no extension is specified) appended to its\n filename.\n\n-i EXT, --backup-ext EXT Create a backup of the input file with ``EXT``\n added to the end of the filename. Implies\n ``--backup-changed`` if neither it nor\n ``--backup-always`` is also given.\n\n-c, --create If the input file does not exist, pretend it's\n empty instead of erroring, and create it with the\n results of the operation. No backup file will be\n created for a nonexistent file, regardless of the\n other options.\n\n If the input file does not exist and no changes are\n made (because ``--backrefs`` was specified and\n ``--regexp`` didn't match), the file will not be\n created.\n\n-L LINE, --line LINE Use ``LINE`` as the line to insert. This option is\n useful when ``LINE`` begins with a hyphen.\n\n-m, --match-first If ``--regexp`` matches, replace the first matching\n line with ``line``.\n\n-M, --match-last If ``--regexp`` matches, replace the last matching\n line with ``line``. This is the default.\n\n-o FILE, --outfile FILE Write the resulting file contents to ``FILE``\n instead of modifying the input file.\n\n It is an error to specify this option with any of\n ``--backup-changed``, ``--backup-always``, or\n ``--backup-ext``.\n\n\n``remove``\n----------\n\n::\n\n lineinfile remove [<options>] <regexp> [<file>]\n lineinfile remove [<options>] -e <regexp> [<file>]\n\nDelete all lines from the given file that match the given `Python regular\nexpression`_.\n\nIf no file name is given on the command line, input is read from standard\ninput, and the result is written to standard output. It is an error to specify\nany of the ``--backup-changed``, ``--backup-always``, or ``--backup-ext``\noptions when no file is given.\n\nOptions\n```````\n\n--backup, --backup-changed If the input file is modified, create a backup of\n the original file. The backup will have the\n extension specified with ``--backup-ext`` (or ``~``\n if no extension is specified) appended to its\n filename.\n\n--backup-always Create a backup of the original file regardless of\n whether or not it's modified. The backup will have\n the extension specified with ``--backup-ext`` (or\n ``~`` if no extension is specified) appended to its\n filename.\n\n-i EXT, --backup-ext EXT Create a backup of the input file with ``EXT``\n added to the end of the filename. Implies\n ``--backup-changed`` if neither it nor\n ``--backup-always`` is also given.\n\n-e REGEX, --regexp REGEX Delete all lines that match ``REGEX``. This option\n is useful when ``REGEX`` begins with a hyphen.\n\n-o FILE, --outfile FILE Write the resulting file contents to ``FILE``\n instead of modifying the input file.\n\n It is an error to specify this option with any of\n ``--backup-changed``, ``--backup-always``, or\n ``--backup-ext``.\n\n\nLibrary API\n===========\n\nNote that all regular expression matching is done with the ``Pattern.search()``\nmethod, i.e., it is not anchored at the start of the line. In order to force a\nregular expression to start matching at the beginning of a line, prefix it with\n``^`` or ``\\A``.\n\n.. code:: python\n\n lineinfile.add_line_to_file(\n filepath: Union[str, bytes, os.PathLike[str], os.PathLike[bytes]],\n line: str,\n regexp: Optional[Union[str, re.Pattern[str]]] = None,\n inserter: Optional[Inserter] = None,\n match_first: bool = False,\n backrefs: bool = False,\n backup: Optional[BackupWhen] = None,\n backup_ext: Optional[str] = None,\n create: bool = False,\n encoding: Optional[str] = None,\n errors: Optional[str] = None,\n ) -> bool\n\nAdd the given ``line`` to the file at ``filepath`` if it is not already\npresent. Returns ``True`` if the file is modified. If ``regexp`` is set to a\nregular expression (either a string or a compiled pattern object) and it\nmatches any lines in the file, ``line`` will replace the last matching line (or\nthe first matching line, if ``match_first=True``). If the regular expression\ndoes not match any lines (or no regular expression is specified) and ``line``\nis not found in the file, the line is inserted at the end of the file by\ndefault; this can be changed by passing the appropriate object as the\n``inserter`` argument; see \"Inserters_\" below.\n\nWhen ``backrefs`` is true, if ``regexp`` matches, the capturing groups in the\nregular expression are used to expand any ``\\n``, ``\\g<n>``, or ``\\g<name>``\nbackreferences in ``line``, and the resulting string replaces the matched line\nin the input. If ``backrefs`` is true and ``regexp`` does not match, the file\nis left unchanged. It is an error to set ``backrefs`` to true without also\nsetting ``regexp``.\n\nWhen ``backup`` is set to ``lineinfile.CHANGED``, a backup of the file's\noriginal contents is created if the file is modified. When ``backup`` is set\nto ``lineinfile.ALWAYS``, a backup is always created, regardless of whether the\nfile is modified. The name of the backup file will be the same as the\noriginal, with the value of ``backup_ext`` (default: ``~``) appended.\n\nIf ``create`` is true and ``filepath`` does not exist, pretend it's empty\ninstead of erroring, and create it with the results of the operation. No\nbackup file will ever be created for a nonexistent file. If ``filepath`` does\nnot exist and no changes are made (because ``backrefs`` was set and ``regexp``\ndidn't match), the file will not be created.\n\n\n.. code:: python\n\n lineinfile.remove_lines_from_file(\n filepath: Union[str, bytes, os.PathLike[str], os.PathLike[bytes]],\n regexp: Union[str, re.Pattern[str]],\n backup: Optional[BackupWhen] = None,\n backup_ext: Optional[str] = None,\n encoding: Optional[str] = None,\n errors: Optional[str] = None,\n ) -> bool\n\nDelete all lines from the file at ``filepath`` that match the regular\nexpression ``regexp`` (either a string or a compiled pattern object). Returns\n``True`` if the file is modified.\n\nWhen ``backup`` is set to ``lineinfile.CHANGED``, a backup of the file's\noriginal contents is created if the file is modified. When ``backup`` is set\nto ``lineinfile.ALWAYS``, a backup is always created, regardless of whether the\nfile is modified. The name of the backup file will be the same as the\noriginal, with the value of ``backup_ext`` (default: ``~``) appended.\n\n\n.. code:: python\n\n lineinfile.add_line_to_string(\n s: str,\n line: str,\n regexp: Optional[Union[str, re.Pattern[str]]] = None,\n inserter: Optional[Inserter] = None,\n match_first: bool = False,\n backrefs: bool = False,\n ) -> str\n\nAdd the given ``line`` to the string ``s`` if it is not already present and\nreturn the result. If ``regexp`` is set to a regular expression (either a\nstring or a compiled pattern object) and it matches any lines in the input,\n``line`` will replace the last matching line (or the first matching line, if\n``match_first=True``). If the regular expression does not match any lines (or\nno regular expression is specified) and ``line`` is not found in the input, the\nline is inserted at the end of the input by default; this can be changed by\npassing the appropriate object as the ``inserter`` argument; see \"Inserters_\"\nbelow.\n\nWhen ``backrefs`` is true, if ``regexp`` matches, the capturing groups in the\nregular expression are used to expand any ``\\n``, ``\\g<n>``, or ``\\g<name>``\nbackreferences in ``line``, and the resulting string replaces the matched line\nin the input. If ``backrefs`` is true and ``regexp`` does not match, the input\nis left unchanged. It is an error to set ``backrefs`` to true without also\nsetting ``regexp``.\n\n\n.. code:: python\n\n lineinfile.remove_lines_from_string(\n s: str,\n regexp: Union[str, re.Pattern[str]],\n ) -> str\n\nDelete all lines from the string ``s`` that match the regular expression\n``regexp`` (either a string or a compiled pattern object) and return the\nresult.\n\n\nInserters\n---------\n\nInserters are objects used by the ``add_line_*`` functions to determine the\nlocation at which to insert ``line`` when it is not found in the input and the\n``regexp`` argument, if set, doesn't match any lines.\n\n``lineinfile`` provides the following inserter classes:\n\n``AtBOF()``\n Always inserts the line at the beginning of the file\n\n``AtEOF()``\n Always inserts the line at the end of the file\n\n``AfterFirst(regexp)``\n Inserts the line after the first input line that matches the given regular\n expression (either a string or a compiled pattern object), or at the end of\n the file if no line matches.\n\n``AfterLast(regexp)``\n Inserts the line after the last input line that matches the given regular\n expression (either a string or a compiled pattern object), or at the end of\n the file if no line matches.\n\n``BeforeFirst(regexp)``\n Inserts the line before the first input line that matches the given regular\n expression (either a string or a compiled pattern object), or at the end of\n the file if no line matches.\n\n``BeforeLast(regexp)``\n Inserts the line before the last input line that matches the given regular\n expression (either a string or a compiled pattern object), or at the end of\n the file if no line matches.\n\n\nHandling of Line Endings\n========================\n\n``lineinfile`` operates on files using Python's universal newlines mode, in\nwhich all LF (``\\n``), CR LF (``\\r\\n``), and CR (``\\r``) sequences in a file\nare converted to just LF when read into a Python string, and LF is in turn\nconverted to the operating system's native line separator when written back to\ndisk.\n\nIn the majority of cases, this allows you to use ``$`` in regular expressions\nand have it always match the end of an input line, regardless of what line\nending the line had on disk. However, when using ``add_line_to_string()`` or\n``remove_lines_from_string()`` with a string with non-LF line separators,\nthings can get tricky. ``lineinfile`` follows the following rules regarding\nline separators:\n\n- Lines are terminated by LF, CR, and CR LF only.\n\n- When an ``add_line_*`` function compares a ``line`` argument against a line\n in the input, the line ending is stripped from both lines. This is a\n deviation from Ansible's behavior, where only the input line is stripped.\n\n- When matching an input line against ``regexp`` or an inserter, line endings\n are not stripped. Note that a regex like ``r\"foo$\"`` will not match a line\n that ends with a non-LF line ending, so this can result in patterns not\n matching where you might na\u00efvely expect them to match.\n\n- When adding a line to the end of a file, if the file does not end with a line\n ending already, an LF is appended before adding the line.\n\n- When adding ``line`` to a document (either as a new line or replacing a\n pre-existing line), LF is appended to the line if it does not already end\n with a line separator; any line ending on the line being replaced (if any) is\n ignored (If you want to preserve it, use backrefs). If the only difference\n between the resulting ``line`` and the line it's replacing is the line\n ending, the replacement still occurs, the line ending is modified, and the\n document is changed.\n",
"bugtrack_url": null,
"license": null,
"summary": "Add & remove lines in files by regex",
"version": "0.4.2",
"project_urls": {
"Bug Tracker": "https://github.com/jwodder/lineinfile/issues",
"Source Code": "https://github.com/jwodder/lineinfile"
},
"split_keywords": [
"add line",
" edit",
" ensure line",
" insert line",
" regexp",
" remove line",
" sed"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "af358f8b010097c5470c347827685be640d4673fa877729b8b832f8b943e8fb5",
"md5": "574d4d77fdf32041f0d6d932d2e43e82",
"sha256": "4e2e82ec7ba9fb355558ca8b797d4a89f7f595ab1a3e4357e297709ec9fdd23c"
},
"downloads": -1,
"filename": "lineinfile-0.4.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "574d4d77fdf32041f0d6d932d2e43e82",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 13349,
"upload_time": "2025-08-02T14:24:32",
"upload_time_iso_8601": "2025-08-02T14:24:32.895507Z",
"url": "https://files.pythonhosted.org/packages/af/35/8f8b010097c5470c347827685be640d4673fa877729b8b832f8b943e8fb5/lineinfile-0.4.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "22cc519301c8f0beba6dfd86b32f624c6fa935cab352aa960cdabc3460e02284",
"md5": "108f7831f9b3df0ce1a7e674ac0e7760",
"sha256": "748737325b743e3fa6612cd02d4eae07abf38d12cc8fb7dd356cbcc8d551f820"
},
"downloads": -1,
"filename": "lineinfile-0.4.2.tar.gz",
"has_sig": false,
"md5_digest": "108f7831f9b3df0ce1a7e674ac0e7760",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 23032,
"upload_time": "2025-08-02T14:24:34",
"upload_time_iso_8601": "2025-08-02T14:24:34.750094Z",
"url": "https://files.pythonhosted.org/packages/22/cc/519301c8f0beba6dfd86b32f624c6fa935cab352aa960cdabc3460e02284/lineinfile-0.4.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-02 14:24:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jwodder",
"github_project": "lineinfile",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "lineinfile"
}