Perl Command line - perl -pi -e - perl

I am trying to find and replace a string in a xml document
This is the code I am using:
perl -pi -e s/xmlNamespaceAware=\"false\"">"/xmlNamespaceAware=\"false\"">""<"Alias">"hostname"<"/Alias">"/g
Error:
syntax error at -e line 1, near "s/xmlNamespaceAware="false">/xmlNamespaceAware="false"><Alias>webtctdm1.ecorp.cat.com</Alias"
If I remove the / in it works fine.
Also is there anyway I can write "<"Alias">"hostname"<"/Alias">" on a newline oppose to the same line as xmlNamespaceAware="false">
Thanks in advance.

Make your life easier and pick a delimiter other than / when dealing with HTML/XML.
With newline literal:
perl -pi -e '
s{xmlNamespaceAware="false">}
{xmlNamespaceAware="false">
<Alias>hostname</Alias>}g' *.xml
With newline escape character:
perl -pi -e '
s{xmlNamespaceAware="false">}
{xmlNamespaceAware="false">\n<Alias>hostname</Alias>}g' *.xml
Both do the same.

You can use the \K escape character for convenience. It will preserve the preceding string, much like a look-around assertion. I am also using # as the delimiter for the substitution regex to avoid having to escape slashes.
perl -pi -e 's#xmlNamespaceAware="false">\K#\n<Alias>hostname</Alias>#g'

That's a hopeless mess of a string. You've forgotten to escape some ", you've got a bare / in </Alias> which actually terminates the regex. Why not just use a single-quoted string to hold the whole thing?
perl -pi -e 's/xmlNamespaceAware="false">/xmlNamespaceAware="false"><Alias>hostname<\/Alias>/g'

I think this should do it for you, but I have no Linux box to hand to try it. Using pipe as a delimiter for s/// avoids the need for escaping slashes.
perl -pi -e 's|xmlNamespaceAware="false">|xmlNamespaceAware="false">\n<Alias>hostname</Alias>|g'

Related

Insert linebreak in a file after a string

I have a unique (to me) situation:
I have a file - file.txt with the following data:
"Line1", "Line2", "Line3", "Line4"
I want to insert a linebreak each time the pattern ", is found.
The output of file.txt shall look like:
"Line1",
"Line2",
"Line3",
"Line4"
I am having a tough time trying to escape ", .
I tried sed -i -e "s/\",/\n/g" file.txt, but I am not getting the desired result.
I am looking for a one liner using either perl or sed.
You may use this gnu sed:
sed -E 's/(",)[[:blank:]]*/\1\n/g' file.txt
"Line1",
"Line2",
"Line3",
"Line4"
Note how you can use single quote in sed command to avoid unnecessary escaping.
If you don't have gnu sed then here is a POSIX compliant sed solution:
sed -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
To save changes inline use:
sed -i.bak -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
Could you please try following. using awk's substitution mechanism here, in case you are ok with awk.
awk -v s1="\"" -v s2="," '{gsub(/",[[:blank:]]+"/,s1 s2 ORS s1)} 1' Input_file
Here's a Perl solution:
perl -pe 's/",\K/\n/g' file.txt
The substitution pattern matches the ",, but the \K says to ignore anything to the left for the replacement (so, ",) will not be replaced. The replacement then effectively inserts the newline.
I used the single quote for the argument to -e, but that doesn't work on Windows where you have to use ". Instead of escaping the ", you can specify it in another way. That's code number 0x22, so you can write:
perl -pe "s/\x22,\K/\n/g" file.txt
Or in octal:
perl -pe "s/\042,\K/\n/g" file.txt
Use this Perl one-liner:
perl -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file > out_file
Or this for in-line replacement:
perl -i.bak -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-n : Loop over the input one line at a time, assigning it to $_ by default.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-a : Split $_ into array #F on whitespace or on the regex specified in -F option.
-F'/"\K,\s*/' : Split into #F on a double quote, followed by comma, followed by 0 or more whitespace characters, rather than on whitespace. \K : Cause the regex engine to "keep" everything it had matched prior to the \K and not include it in the match. This causes to keep the double quote in #F elements, while comma and whitespace are removed during the split.
-i.bak : Edit input files in-place (overwrite the input file). Before overwriting, save a backup copy of the original file by appending to its name the extension .bak.
SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches
perldoc perlrequick: Perl regular expressions quick start

I want to replace last / by ,

I want to replace this:
a/b/c|d,385|386|387|388|389|390|391|392|393|394|395|396|397|398|399|400/0.162,214|229|254|255|270|272|276|287|346|356|361|362|365|366|367|369/0.18,improve/11.11,
With:
a/b/c|d,385|386|387|388|389|390|391|392|393|394|395|396|397|398|399|400/0.162,214|229|254|255|270|272|276|287|346|356|361|362|365|366|367|369/0.18,improve,11.11,
With this sed command:
sed -i 's/\(.*\)\//\1,/'
This works in Unix. I tried to use this with system in Perl code, but it doesnt work. I request a solution using sed in Perl for the same.
First of all, the code you claim works doesn't.
$ printf 'a/b/c\n' | sed 's/(.*)//\1,/'
sed: -e expression #1, char 9: unknown option to `s'
It should be
$ printf 'a/b/c\n' | sed 's/\(.*\)\//\1,/'
a/b,c
You're asking how to execute this command from Perl. You can use the following:
system('sed', '-i', '/\\(.*\\)\\//\\1,/', '--', $qfn)
Note that you can quite easily do the same task in Perl itself.
local #ARGV = $qfn;
local $^I = '';
while (<>) {
s{^.*\K/}{,};
print;
}
Here is way to do this in sed:
echo "365|366|367|369/0.18,improve/11.11," | sed 's/^\(.*\)\/\(.*\)$/\1,\2/'
365|366|367|369/0.18,improve,11.11,
The regex pattern used is:
^\(.*\)\/\(.*\)$
This says to match and capture everything up until the last forward slash. Then, also match and capture everything after the last forward slash. Finally replace with the first two capture groups, but now separated by a comma.
Notes:
forward slash / needs to be escaped by a backslash, to distinguish it from being the pattern delimiter
parentheses in the capture groups also need to be escaped with backslash

Escaping a variable with special characters within sed - comment and uncomment an arbitrary line of source code

I need to comment out a line in a crontab file through a script, so it contains directories, spaces and symbols. This specific line is stored in a variable and I am starting to get mixed up on how to escape the variable. Since the line changes on a regular basis I dont want any escaping in there. I don't want to simply add # in front of it, since I also need to switch it around and replace the line again with the original without the #.
So the goal is to replace $line with #$line (comment) with the possibility to do it the other way around (uncomment).
So I have a variable:
line="* * * hello/this/line & /still/this/line"
This is a line that occurs in a file, file.txt. Wich needs to get comment out.
First try:
sed -i "s/^${line}/#${line}/" file.txt
Second try:
sed -i 's|'${line}'|'"#${line}"'|g' file.txt
choroba's helpful answer shows an effective solution using perl.
sed solution
If you want to use sed, you must use a separate sed command just to escape the $line variable value, because sed has no built-in way to escape strings for use as literals in a regex context:
lineEscaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$line") # escape $line for use in regex
sed -i "s/^$lineEscaped\$/#&/" file.txt # Note the \$ to escape the end-of-line anchor $
With BSD/macOS sed, use -i '' instead of just -i for in-place updating without backup.
And the reverse (un-commenting):
sed -i "s/^#\($lineEscaped\)\$/\1/" file.txt
See this answer of mine for an explanation of the sed command used for escaping, which should work with any input string.
Also note how variable $lineEscaped is only referenced once, in the regex portion of the s command, whereas the substitution-string portion simply references what the regex matched (which avoids the need to escape the variable again, using different rules):
& in the substitution string represents the entire match, and \1 the first capture group (parenthesized subexpression, \(...\)).
For simplicity, the second sed command uses double quotes in order to embed the value of shell variable $lineEscaped in the sed script, but it is generally preferable to use single-quoted scripts so as to avoid confusion between what the shell interprets up front vs. what sed ends up seeing.
For instance, $ is special to both the shell and sed, and in the above script the end-of-line anchor $ in the sed regex must therefore be escaped as \$ to prevent the shell from interpreting it.
One way to avoid confusion is to selectively splice double-quoted shell-variable references into the otherwise single-quoted script:
sed -i 's/^'"$lineEscaped"'$/#&/' file.txt
awk solution
awk offers literal string matching, which obviates the need for escaping:
awk -v line="$line" '$0 == line { $0 = "#" $0 } 1' file.txt > $$.tmp && mv $$.tmp file.txt
If you have GNU Awk v4.1+, you can use -i inplace for in-place updating.
And the reverse (un-commenting):
awk -v line="#$line" '$0 == line { $0 = substr($0, 2) } 1' file.txt > $$.tmp &&
mv $$.tmp file.txt
Perl has ways to do the quoting/escaping for you:
line=$line perl -i~ -pe '$regex = quotemeta $ENV{line}; s/^$regex/#$ENV{line}/' -- input.txt

Perl one-liner to remove trailing space from a file

I am calling a perl one-liner inside a Perl script.
The intention of the one-liner is to remove the trailing space from a file.
Inside the main perl script:
`perl -pi -e 's/\s+$//' tape.txt`;
Now it is throwing me an error Substitution replacement not terminated at -e line 2.
Where is it going wrong?
It's because of the $/ (special variable) inside your main perl script. Note that variables are interpolated inside `` strings just like inside "" strings, and the fact that there are some single quotes in there doesn't change that. You need to escape that $:
`perl -pi -e 's/\s+\$//' tape.txt;`
The backtick syntax invokes a shell and when invoked, the shell assumes it should interpolate the string passed.
A cleaner syntax might be:
system('perl -pli -e "s/\s*$//" tape.txt');
Since you aren't capturing the output of the command, using backticks or qx in lieu of system isn't an issue.
Too, adding the -l switch autochomps each line read and then adds a newline back --- probably what you want.
\s matches [ \t\n\r\f] and do not want to match \n.
Notice use of {} for subst delimiters:
$ echo -e 'hi \nbye'| perl -pe 's{[\t\040]+$}{};' | cat -A
hi$
bye$

How to delete specfic text in a file using a perl one liner?

I'm trying to find some text in an XML file and delete only a part of a line that has it.
I found this format to try: perl -p -i -e "s/$1/$2/g" $3 after some code searches.
So I'm using this code:
perl -p -i.bak -e "s/\'../../../specialText/\'//g" "C:/box/fileName.XML";
What I want to do is delete everything from the inner single quotes as in:
'../../../specialText/', but using q() or \' to escape the quote doesn't work and I'm not sure the ..'s aren't messing things up either. I'm guessing that not putting anything in as a text replacement will delete it properly, but I'm not sure.
The errors are:
Backslash found where operator expected at -e line 1, near "/specialText/\"
(Missing operator before \?)
syntax error at -e line 1, near "/specialText/\"
Can't find string terminator "'" anywhere before EOF at -e line 1.
How do rewrite this one liner to accomplish this?
This works.
C:\box>perl -p -i.bak -e s/Copyright/bar/g Test.txt
I tried it on another file, so now I just have to play with it to modify my original.
You can escape the . and / characters in the search string by putting a backslash (\) before each of them.
However, to avoid acute leaning toothpick syndrome, I'd recommend instead using alternative regexp delimiters and the \Q and \E escape sequences, like this:
perl -p -i.bak -e "s(\Q'../../../specialText/'\E)()g" "C:/box/fileName.XML"
And what's wrong with using another set of delimiters?
perl -p -i.bak -e "s{'../../../specialText/'}{}g" "C:/box/fileName.XML"