Find and replace over multiple lines using sed in

2019-05-28 16:29发布

I have a Cocos2D tmx file which is very much like an xml and includes carriage returns and spaces.

My requirements are:

In every tmx file in Resources/maps_sideScrolling/

find and everything between

<tileset firstgid="1"

and the first occurring

<layer name="background"

and replace with the contents of Resources/maps_sideScrolling/tileProperties.txt

I've tried the following with no result. The problem is caused by the string to be searched has multiple lines.

sed -i '' 's{<tileset firstgid="1.*<layer name="background"{Resources/maps_sideScrolling/tileProperties.txt{g' Resources/maps_sideScrolling/*.tmx;

Here's a pastebin of the tmx snippet that I want to edit: http://pastebin.com/wr39zj1r

4条回答
姐就是有狂的资本
2楼-- · 2019-05-28 16:58
$ cat file1
abc
def
ghi

$ cat file2
pre
stuff
<tileset firstgid="1"
foo
bar
<layer name="background"
post
stuff

$ awk '
   NR==FNR { s=s ORS $0; next }
   /<tileset firstgid="1"/    { print $0 s; f=1 }
   /<layer name="background"/ { f=0 }
   !f
' file1 file2
pre
stuff
<tileset firstgid="1"
abc
def
ghi
<layer name="background"
post
stuff

Note the above replaces foo\nbar in file2 with the contents of file1 based on the delimiters you wanted. If you want to remove the delimiting lines too, that's a trivial tweak.

查看更多
Deceive 欺骗
3楼-- · 2019-05-28 17:00

Here's one way using sed. Run like:

sed -f script.sed Resources/maps_sideScrolling/*.tmx

Contents of script.sed:

/<tileset firstgid="1"/ {

    s/\(<tileset firstgid="1"\).*/\1/

    r Resources/maps_sideScrolling/tileProperties.txt

    p

    :a
    N
    s/.*\(<layer name="background".*\)/\1/
    t
    ba
}

Please let me know how it goes. I don't have access to a machine with BSD sed at the moment so I haven't been able to test it properly. However, my gut feeling is that it should work for you.

查看更多
虎瘦雄心在
4楼-- · 2019-05-28 17:19
#!/usr/bin/env bash

sed -rni '/<tileset\s+firstgid="1"/ {
s/(\s*<tileset\s+firstgid="1").*/\1/p
r /home/username/ololofile
:loop
n
s/.*<layer name="background".*/&/p
t
b loop
}' ~/files/to/replace/*

sed with -n option will output nothing until print (p) is forced or there is some file reading.

There were suddenly errors when I tried to run the script with comments, so deleted my post, but then figured out that spaces between the file name to read with r command and commentary are treated as continuation to the file name so I removed comments and will describe the script commands for sed line by line now:

  1. look for a pattern to begin with;
  2. at the second line useful part of the found line is printed out;
  3. then goes reading from file;
  4. next line contains label, since now we read and search line by line;
  5. line matching the end pattern found and printed;
  6. because we need only first matching end pattern, in case of a successful replace go to the end of the script;
  7. if no replace was performed, there was no jump to the end and goes jump to loop label where a new line will attempt to load into the pattern space.
查看更多
Lonely孤独者°
5楼-- · 2019-05-28 17:24

Geek uses python to do this kind of thing to TMX map files. Just an option to consider.

Something like this (but iterating all files in directory etc), and save it as a .sh file:

#!/usr/bin/env python

import re

#you'd open a file and read in the tile properties thing
fakeTileProperties = "<tileproperties>1</tileproperties>\r"

f = open( "file1.tmx", "rU")
fo = open( "outputfile.tmx", "wc");

#read source file
s = f.read();

#find what you need
m = re.search("([\W\w]*)(<tileset firstgid=\"1\"[\W\w]*)(<layer name=\"background\"[\W\w]*)", s )

#write out to source file
fo.write(m.group(1))
fo.write(fakeTileProperties)
fo.write(m.group(3));

f.close();
fo.close();

print "done!"

The code handles content before the tile set firstgid="1" just in case there is some.

To use a script like this in Xcode 4 do the following:

  • put your script in a file next to your project file, name it myscript.py
  • use chmod +x myscript.py to make the script file executable.
  • in your Xcode project select the project and target and "Build Phases" tab and then create a new "Run Script" build phase.
  • leave the default shell of /bin/sh
  • put the following into the script field: $(SOURCE_ROOT)/myscript.py

Then when you do a build you should see the python script get executed. You can do a really simple test python file to test this (I just did):

#!/usr/bin/env python

print 'hello from python!'

note that the setting in the Run Script setup "Show Environmental variables in build log" is very helpful for getting the environmental variables like SOURCE_ROOT and such to locate your files.

good luck!

查看更多
登录 后发表回答