Search code examples
windowsperl

How do I add new lines after deleting a large amount of text in Perl windows?


I'm trying to remove a large amount of text from a file before inserting a few new lines. I can delete everything after the word 'CParticleSystemDefinition' with a single line of code like this

perl -0777 -pi -we "s/CParticleSystemDefinition\x22\K.*/\n}/s" "D:\Steam\steamapps\common\dota 2 beta\content\dota_addons\custom\particles\generic_gameplay\winter_effects_creep.vpcf"

But when I try to change the code slightly so that it adds a few new lines like this, it doesn't work

perl -0777 -pi -we "s/CParticleSystemDefinition\x22\K.*/\n      m_Children = \n    [\n        {\n            m_ChildRef = resource:\x22particles/generic_gameplay/winter_effects_breath.vpcf\x22\n        },\n    ]\n}/s" "D:\Steam\steamapps\common\dota 2 beta\content\dota_addons\custom\particles\generic_gameplay\winter_effects_creep.vpcf"

So, basically, what I want to do is make this file

{
    _class = "CParticleSystemDefinition"
    m_bShouldHitboxesFallbackToRenderBounds = false
    m_nMaxParticles = 24
    m_flConstantRadius = 15.000000
    m_flConstantLifespan = 0.500000
    m_ConstantColor = 
    [
        212,
        170,
        145,
        255,
    ]
    m_bShouldSort = false
    m_Renderers = 
    [
        {
            _class = "C_OP_RenderSprites"
            m_nSequenceCombineMode = "SEQUENCE_COMBINE_MODE_USE_SEQUENCE_0"
            m_bMod2X = true
            m_nOrientationType = 3
            m_hTexture = resource:"materials/particle/footprints/footprints_generic.vtex"
            m_flAnimationRate = 1.000000
        },
    ]
    m_Emitters = 
    [
        {
            _class = "C_OP_ContinuousEmitter"
            m_flEmitRate = 10.000000
            m_flStartTime = 0.500000
            m_nScaleControlPoint = 5
        },
    ]
}

look like this

{
    _class = "CParticleSystemDefinition"
    m_Children = 
    [
        {
            m_ChildRef = resource:"particles/generic_gameplay/winter_effects_breath.vpcf"
        },
    ]
}

Solution

  • Do it in two steps -- clear the rest of the file after that phrase, then add the desired text

    perl -0777 -i.bak -wpe"s{Definition\x22\K.*}{}s; $_ .= qq(\n\tm_Children...)"  file
    

    where I've used ellipses to indicate the rest, for clarity. I added .bak to keep a backup file, until this is tested well enough.

    Adding a string in the replacement part is fine as well of course -- I don't readily see what fails (and how?) in your code. Breaking it up into two steps simply makes it easier to review and organize it better but one can also run that code in the replacement part, using /e modifier

    perl -0777 -i.bak -wpe"
        s{Definition\x22\K.*}{
            # any valid Perl code, what it evaluates to is used as replacement
            qq(\n\tm_Children...)
        }es;
    "  file
    

    If you don't want tabs, which may or may not get expanded depending on various settings and on what's done with this, can prepare and use a string of spaces instead. Then we might as well build the replacement more systematically

    perl -0777 -i.bak -wpe"
        s{Definition\x22\K.*}{}s; 
        $s4 = q( ) x 4;  # four spaces
        $_ .= qq(\n${s4}m_Children =\n$s4) . join qq(\n$s4), 
            q([),
            q({), 
            qq($s4).q(m_ChildRef = ...)  # etc
            qq(\n)
    " file
    

    Now one can either make this into a better system (adding a suitable programming construct for each new level of indentation for example, like map over such lines so to add indentation to all in one statement), if there is a lot -- or condense it if there's really just a few lines.

    Again, this can run inside the regex's replacement side, with the additional /e modifier.


    This can be done line-by-line in one pass as well, using the read-write (+<) mode for open

    perl -MPath::Tiny -wE"
        $f = shift // die qq(Need a filename); 
        open $fh, qq(+<), $f or die qq(Cant open $f: $!);
        while (<$fh>) { last if /Definition\x22$/ };       # past the spot to keep
        truncate $fh, tell($fh);                           # remove the rest
        say qq(File now:\n), path($f)->slurp;              # (just to see it now)
        say $fh $_ for                                     # add new content
            qq(new line 1), 
            qq(new line 2)  
    " filename
    

    (Carefully with read-write modes. Please read the docs with care first.)