o nnh.@sddlmZdZdZdZddlZddlZddlZddlm Z ddl m Z m Z ddl mZd d Zd d Zd dZedkrAedSdS)) annotationsa --- module: replace author: Evan Kaufman (@EvanK) extends_documentation_fragment: - action_common_attributes - action_common_attributes.files - files - validate attributes: check_mode: support: full diff_mode: support: full platform: platforms: posix safe_file_operations: support: full vault: support: none short_description: Replace all instances of a particular string in a file using a back-referenced regular expression description: - This module will replace all instances of a pattern within a file. - It is up to the user to maintain idempotence by ensuring that the same pattern would never match any replacements made. version_added: "1.6" options: path: description: - The file to modify. - Before Ansible 2.3 this option was only usable as O(dest), O(destfile) and O(name). type: path required: true aliases: [ dest, destfile, name ] regexp: description: - The regular expression to look for in the contents of the file. - Uses Python regular expressions; see U(https://docs.python.org/3/library/re.html). - Uses MULTILINE mode, which means V(^) and V($) match the beginning and end of the file, as well as the beginning and end respectively of I(each line) of the file. - Does not use DOTALL, which means the V(.) special character matches any character I(except newlines). A common mistake is to assume that a negated character set like V([^#]) will also not match newlines. - In order to exclude newlines, they must be added to the set like V([^#\\n]). - Note that, as of Ansible 2.0, short form tasks should have any escape sequences backslash-escaped in order to prevent them being parsed as string literal escapes. See the examples. type: str required: true replace: description: - The string to replace regexp matches. - May contain backreferences that will get expanded with the regexp capture groups if the regexp matches. - If not set, matches are removed entirely. - Backreferences can be used ambiguously like V(\\1), or explicitly like V(\\g<1>). type: str default: '' after: description: - If specified, only content after this match will be replaced/removed. - Can be used in combination with O(before). - Uses Python regular expressions; see U(https://docs.python.org/3/library/re.html). - Uses DOTALL, which means the V(.) special character I(can match newlines). - Does not use MULTILINE, so V(^) and V($) will only match the beginning and end of the file. type: str version_added: "2.4" before: description: - If specified, only content before this match will be replaced/removed. - Can be used in combination with O(after). - Uses Python regular expressions; see U(https://docs.python.org/3/library/re.html). - Uses DOTALL, which means the V(.) special character I(can match newlines). - Does not use MULTILINE, so V(^) and V($) will only match the beginning and end of the file. type: str version_added: "2.4" backup: description: - Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly. type: bool default: no others: description: - All arguments accepted by the M(ansible.builtin.file) module also work here. type: str encoding: description: - The character encoding for reading and writing the file. type: str default: utf-8 version_added: "2.4" notes: - As of Ansible 2.3, the O(dest) option has been changed to O(path) as default, but O(dest) still works as well. - As of Ansible 2.7.10, the combined use of O(before) and O(after) works properly. If you were relying on the previous incorrect behavior, you may be need to adjust your tasks. See U(https://github.com/ansible/ansible/issues/31354) for details. - Option O(ignore:follow) has been removed in Ansible 2.5, because this module modifies the contents of the file so O(ignore:follow=no) does not make sense. a( - name: Replace old hostname with new hostname (requires Ansible >= 2.4) ansible.builtin.replace: path: /etc/hosts regexp: '(\s+)old\.host\.name(\s+.*)?$' replace: '\1new.host.name\2' - name: Replace after the expression till the end of the file (requires Ansible >= 2.4) ansible.builtin.replace: path: /etc/apache2/sites-available/default.conf after: 'NameVirtualHost [*]' regexp: '^(.+)$' replace: '# \1' - name: Replace before the expression from the beginning of the file (requires Ansible >= 2.4) ansible.builtin.replace: path: /etc/apache2/sites-available/default.conf before: '# live site config' regexp: '^(.+)$' replace: '# \1' # Prior to Ansible 2.7.10, using before and after in combination did the opposite of what was intended. # see https://github.com/ansible/ansible/issues/31354 for details. # Note (?m) which turns on MULTILINE mode so ^ matches any line's beginning - name: Replace between the expressions (requires Ansible >= 2.4) ansible.builtin.replace: path: /etc/hosts after: '(?m)^' before: '' regexp: '^(.+)$' replace: '# \1' - name: Supports common file attributes ansible.builtin.replace: path: /home/jdoe/.ssh/known_hosts regexp: '^old\.host\.name[^\n]*\n' owner: jdoe group: jdoe mode: '0644' - name: Supports a validate command ansible.builtin.replace: path: /etc/apache/ports regexp: '^(NameVirtualHost|Listen)\s+80\s*$' replace: '\1 127.0.0.1:8080' validate: '/usr/sbin/apache2ctl -f %s -t' - name: Short form task (in ansible 2+) necessitates backslash-escaped sequences ansible.builtin.replace: path=/etc/hosts regexp='\\b(localhost)(\\d*)\\b' replace='\\1\\2.localdomain\\2 \\1\\2' - name: Long form task does not ansible.builtin.replace: path: /etc/hosts regexp: '\b(localhost)(\d*)\b' replace: '\1\2.localdomain\2 \1\2' - name: Explicitly specifying positional matched groups in replacement ansible.builtin.replace: path: /etc/ssh/sshd_config regexp: '^(ListenAddress[ ]+)[^\n]+$' replace: '\g<1>0.0.0.0' - name: Explicitly specifying named matched groups ansible.builtin.replace: path: /etc/ssh/sshd_config regexp: '^(?PListenAddress[ ]+)(?P[^\n]+)$' replace: '#\g\g\n\g0.0.0.0' #N) format_exc)to_textto_bytes) AnsibleModulec Cstj|jd\}}t|d}||||jdd}| }|rLd|vr0|j d|d| ||\}} } |dk}|dkrL|j d|| fd|r[|j |||jd d dSdS) N)dirwbvalidatez%szvalidate must contain %%s: %s)msgrz"failed to validate: rc:%s error:%s unsafe_writes)r ) tempfilemkstemptmpdirosfdopenwritecloseparamsget fail_json run_command atomic_move) modulecontentspathtmpfdtmpfilefr validrcouterrr#B/usr/local/lib/python3.10/dist-packages/ansible/modules/replace.py write_changess$  r%cCs8||j}||dr|r|d7}d}|d7}||fS)NFz and Tz,ownership, perms or SE linux context changed)load_file_common_argumentsr set_file_attributes_if_different)rchangedmessage file_argsr#r#r$check_file_attrss  r+c Cstttddgddtdddtdddtdd tdd td d dtdd tdd dd ddd}|j}|d}|d}tdd}t|dddd|d<t|dddd|d<t|dddd|d<t|dddd|d<tj|r||jdd|dtj|s|jdd|dnCz t |d}t| d|d}Wdn1swYWn"t t fy}z|jd |t|ft d!WYd}~nd}~wwd}|dr|drd"|d|df}n|drd#|d}n |drd$|d}|r/t|tj} t| |} | r| d%} | d%| d%g} nd&||d'<d |d(<|jd1i|n|} t|dtj} z t| |d| d}Wn tjyf}z|jd)t|t d!WYd}~nd}~ww|d*dkr| |dkr|r|d| d|d|| d*d|d*f}d+|d*}d}|jr||||dd,|d-<nd}d }|r|js|d.rtj|r|||d/<tj|}t|t|d|d0|t|||\|d'<|d(<|jd1i|dS)2NrT)destdestfilename)typerequiredaliasesstr)r/r0)r/default)r/boolFzutf-8)rregexpreplaceafterbeforebackupr encoding) argument_specadd_file_common_argssupports_check_moder;r)r r8surrogate_or_strictpassthru)errors nonstringr9r6r7zPath %s is a directory !)r r izPath %s does not exist !rb)rAr;z%Unable to read the contents of %s: %s)r exceptionz%s(?P.*?)%sz%s(?P.*)z(?P.*)%s subsectionz@Pattern for before/after params did not match the given file: %sr r(z*Unable to process replace due to error: %sz%s replacements made) before_headerr9 after_headerr8diffr: backup_file)r;r#) rdictrrrrisdirrexistsopenreadOSErrorIOErrorrrecompileDOTALLsearchgroupstartend exit_json MULTILINEsubnerror_diff check_mode backup_localrealpathr%rr+)rrrr;res_argsrrepattern section_rematchsectionindicesmreresultr r(r#r#r$mains             0   rk__main__) __future__r DOCUMENTATIONEXAMPLESRETURNrrSr tracebackr+ansible.module_utils.common.text.convertersrransible.module_utils.basicrr%r+rk__name__r#r#r#r$s iE   ^