Awk to replace single quote

2019-03-16 19:52发布

问题:

I want to replace all include('./ in a set of files with include('. I am trying to use awk as follows:

awk '{gsub("include\('"'"'./", "include\('"'"'", $0); print > FILENAME}' *.php

It throws me this error.

awk: (FILENAME=xyz.php FNR=1) fatal: Unmatched ( or \(: /include('.//

Any help would be appreciated.

回答1:

@OP, you can try using octal code for the single quote(\047) and forward slash(\057), eg

$ cat file
include('./
$ awk '{gsub(/include\(\047\.\057/ , "include(\047" ) }1' file
include('


回答2:

This works (without the I/O redirection on the 'print'):

awk '{gsub(/include\('"'"'.\//, "include\('"'"'", $0); print }' # Wrong
awk '{gsub(/include\('"'"'.\//, "include('"'"'", $0); print }'  # Right

It maps this input:

include('./abc')
include('x/abc')

to:

include('abc')
include('abc')

Empirically, it seems that the regular expression must be inside slashes; the replacement string must be a regular string. You will need to map the '.' to '\.' to stop the second replacement.

I'm not very happy with this explanation. The man page for 'awk' on MacOS X says:

/re/ is a constant regular expression; any string (constant or variable) may be used as a regular expression, except in the position of an isolated regular expression in a pattern.

So, in theory, the string form you used should work. Empirically, it didn't; I got substantially the same error message as you did with your code. And you had got the shell quotes correct, which is non-trivial.

There are times when Perl might be easier (because you can choose an arbitrary delimiter to mark the regex boundaries):

perl -pe "s%include\('\./%include('%g"


回答3:

Try this :

awk '{gsub("include(\'"'"'./", "include\('"'"'", $0); print > FILENAME}' *.php

you misplaced backslash

or this :

 awk '{gsub("include(\'./", "include(\'", $0); print > FILENAME}' *.php

how about this ?

awk '{gsub("include(\47./", "include(\47", $0); print > FILENAME}' *.php

Did you try without esacping anything

awk '{gsub("include('./", "include('", $0); print > FILENAME}' *.php


回答4:

You don't need to use awk if all you want to do is this. :) Also, writing to a file as you're reading from it, in the way that you did, will lead to data loss or corruption, try not to do it.

for file in *.php ; do
# or, to do this to all php files recursively:
# find . -name '*.php' | while read file ; do
  # make backup copy; do not overwrite backup if backup already exists
  test -f $file.orig || cp -p $file $file.orig
  # awk '{... print > NEWFILE}' NEWFILE="$file" "$file.orig"
  sed -e "s:include('\./:include(':g" "$file.orig" >"$file"
done

Just to clarify the data loss aspect: when awk (or sed) start processing a file and you ask them to read the first line, they will actually perform a buffered read, that is, they will read from the filesystem (let's simplify and say "from disk") a block of data as large as their internal read buffer (e.g. 4-65KB) in order to get better performance (by reducing disk I/O.) Assume that the file you're working with is larger than the buffer size. Further reads will continue to come from the buffer until the buffer is exhausted, at which point a second block of data will be loaded from disk into the buffer etc.

However, just after you read the first line, i.e. after the first block of data is read from disk into the buffer, your awk script opens FILENAME, the input file itself, for writing with truncation, i.e. the file's size on disk is reset to 0. At this point all that remains of your original file are the first few kilobytes of data in awk's memory. Awk will merrily continue to read line after line from the in-memory buffer and produce output until the buffer is exhausted, at which point awk will probably stop and leave you with a 4-65k file.

As a side note, if you are actually using awk to expand (e.g. print "PREFIX: " $0), not shrink (gsub(/.../, "")), data, then you'll almost certainly end up with a non-responsive awk and a perpetually growing file. :)



标签: unix shell awk