Excluding lines that contain a pattern before replacing in Perl - regex

I have the following code to replace version string from a set of files
ack --ignore-file=is:HISTORY.md -l --print0 '1\.1\.1' | xargs -0 perl -pi -e 's/1\.1\.1/1\.1\.2/g'
Now, I realized there are some lines in the doxygen comment that also have the version string like this.
/**
* Generate Tag id from Tag name
*
* #since 1.1.1
* #static
* #access public
*
*/
How can I modify the above snippet so that lines that contain #since will be excluded?

To exclude lines with #since you could try this instead of your current perl replace code:
!/\#since/ && s/1\.1\.1/1.1.2/g
or even
/\#since/ || s/1\.1\.1/1.1.2/g

Related

Read file until regex in changelog file using bash

I want to get content in file until find a regex in content. I need this to report my last changelog
Example:
## <small>0.27.3 (2019-03-18)</small>
* Fix integration tests
* Change log message
## <small>0.27.2 (2019-03-18)</small>
* Change find to filter
* Fix bug in typo
* Format message in request
I want a regex to return oly the content of my latest version. Example:
## <small>0.27.3 (2019-03-18)</small>
* Fix integration tests
* Change log message
How can I make this using sed, grep or awk?
Thanks for this
Edit:
I can made it:
CHANGELOG_MESSAGE=$(head -n 1 CHANGELOG.md)$(head -n 20 CHANGELOG.md | tail -n 19 | sed '/##/q' | awk '!/##/')
I think that this solution is a few complex, but works
try this:
sed '1p;1,/^##/!d;/##/d' CHANGELOG.md
explanation
1p # print first line
;
1,/^##/!d # delete everything after second ##-line
;
/##/d # delete all lines start with ##
output
## <small>0.27.3 (2019-03-18)</small>
* Fix integration tests
* Change log message

Insert a text before slash "/" character in bash scripts

I have a line in a file as below:
2 14 * * * /run/opt/server/autoi.sh
And I want to insert "root" before /run/opt/server/autoi.sh as below:
2 14 * * * root /run/opt/server/autoi.sh
I have tried the following command
sed '//run/i root' filename
but it gives the following error:
sed: -e expression #1, char 0: no previous regular expression
Could you please help me to find a fix for it?
Use a different separator character, and you need to use the s command to substitute, e.g:
sed 's#/run#root /run#' filename
or without repeating /run:
sed 's#/run#root &#' filename
You are using the wrong sed command. The i command will insert a new line before the matching line, and of course, //run is not a valid regex at all.
The general form of a sed command is
<address> <action>
where address could be a regex or a line number, and action is a command.
In fact, you want an action without an address, which means it will be applied to every input line; and the action you want to perform is a substitution.
sed 's%/run%root &%' filename
We are using the & convenience shorthand to repeat the string which matched the first regex, and an alternate regex separator instead of / so that / does not itself get interpreted as a regex separator (equivalently, you could backslash-escape it, but here, that produces something called leaning toothpick syndrome).
This will print the results to standard output, not modify the file. Once you have verified that you get the results you want, you might want to add an -i option to modify the input file. (On some platforms, such as *BSD -- which includes MacOS -- you need -i '' with an empty argument.)
Simply substitute very first / with root / as follows.
sed 's/\//root \//' Input_file
sed -e 's/run/root\/run/' abc
Example:
[root#myIP tmp]# cat abc
2 14 * * * /run/opt/server/autoi.sh
[root#myIP tmp]# sed -e 's/run/root\/run/' abc
2 14 * * * /root/run/opt/server/autoi.sh
Edited: To add a username. this should do the trick
[root#myIP tmp]# sed -e 's/\//root \//' abc
2 14 * * * root /run/opt/server/autoi.sh

Extract section of file to a variable, from within a shell script

I'm writing a script intended to run under bash in OS X. I have Markdown files that look like this:
# File name
## Heading 1
Some text
## Heading 2
* List item 1
* List item 2
## Some other section
...
I'm trying to read everything inside Heading 2 into a variable, and I've tried using sed, grep, and perl, but can't get a working solution. Of those tools, it looked to (in theory) be possible and easiest with Perl, especially given that I need multiple lines. It looks like this regex works (at least with javascript syntax):
## Heading 2\s+(.+)\s+
I'd like to keep it a one-liner, and stick to only tools available on stock OS X (El Capitan, 10.11). Assume I only know "Heading 2" up front, not the following header caption.
With sed :
$ myvar=$(sed "/^## $1$/,/^## Heading/!d;//d;/^$/d" file)
$ echo "$myvar"
* List item 1
* List item 2
You can remove /^$/d if you want to preserve blank lines.
Update :
I've replaced single quotes with double quotes to allow shell expansion.
You can call it with ./scriptname.sh "Heading 2".
Some explanations :
/^## $1$/,/^## Heading/ applies subsequent commands to lines matching the first pattern up to next line containing the second pattern.
!d deletes all lines except those corresponding to the range.
//d matches the same pattern as the address(es) and removes it.
Using sed:
head2="$(sed -n '/## Heading 2/,/## Heading 3/{s/^## Heading .*//;p;}' file)"
echo "$head2"
* List item 1
* List item 2
Using perl:
head2="$(perl -0pe 's/(?s).*## Heading 2\s*(.*)\s*## Heading 3.*/\1/' file)"
echo "$head2"
* List item 1
* List item 2
You can also install gnu grep using home brew and use this regex:
head2="$(grep -zoP '## Heading 2\s*\K[\s\S]*(?=\s*## Heading 3)' file)"
You can use awk ranges:
awk '/^## Heading 2/,/^## Heading [^2]/ {if (!/^## Heading 2/&&!/^## Heading [^2]/) { print}}'
or with variables
awk '/s/,/e/ {if ($0 !~ s && $0 !~ e) { print}}' s='^## Heading 2' e='^## Heading [^2]'

Recursive multi-line replace: changing copyright headers

I'm retrying to replace all of the copyright headers in my project (100+ files) with a new version. Currently I have something like this at the start of each file:
<?php
/**
* Project name
*
* #copyright Apache 2.0
* #author FooBar
*/
And I want all my files to start like this:
<?php
/**
* Copyright 2014 FooBar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
I've already looked at:
this thread, which I can't get working. It does a partial replacement, keeping certain lines of the original text in the new text. I want a complete replacement.
this script, which similarly doesn't work for my use case. It replaces the very start of each file with a new header, which causes the existing content (<?php /** */) to be appended to the new comment, thereby causing parse errors.
Does anybody know how I can do a recursive multi-line file replace? Do I need to use sed/awk?
SOLUTION:
I just need to execute this bash script:
INPUT=../path
find $INPUT -name "*.php" -exec sed -i -e '2,/\*\//d; 1r copyright.txt' {} \;
Is it safe to assume all your files start with
<?php
/**
If so, you can use
sed '2,/\*\//d; 1r newSig.txt' input.txt
The first sed command deletes the signature from line 2 til the end of the signature. You could use a dynamic range but it would also delete other multi-line signatures in the file. The second command reads the file newSig.txt, which has your new signature, and appends it after line 1.
With GNU awk for a multi-char RS to read the whole file as a single string:
$ gawk -v RS='^$' -v hdr="\
/**
* Copyright 2014 FooBar
*
* Licensed under the blah blah blah
*/\
" '{sub(/\/\*[^/]+\*\//,hdr)}1' file
<?php
/**
* Copyright 2014 FooBar
*
* Licensed under the blah blah blah
*/
NOTE: you should read Ed Morton's comment too. Regarding that is a problem, you can check the files and only pass the readable ones in you for cycle before running the awk script.
If your files always start like this, one way to solve it with gawk is
awk 'FNR==1 { print $0
print "INSERT YOUR new header here even on multiline print statements."
# if you don't mind your old header, stop here and skip the below rules
}
FNR==2 && $0 ~ "/\*\*" {
while (getline) {
if ($0 == "*/") { getline ; break }
}
}
FNR>2 { print $0 }' INPUTFILE
And you can wrap it in for cycle, like
for file in *php ; do
awk ... $file > $file.new
done
My way is no limit on fix line of
<?php
/**
it will replace the first pair of /** to next **/
1) Save the replace content into file: update.txt (not set suffix to php)
2) then run this command on one php file (abc.php) to confirm first
sed ':a;$!{N;ba};s!/[^/]*/!########!1' abc.php|sed -e '/########/{r update.txt' -e 'd}'
3) if it is fine, then run the script on all php files:
for file in *.php
do
sed ':a;$!{N;ba};s!/[^/]*/!########!1' $file|sed -e '/########/{r update.txt' -e 'd}' > temp
mv temp "$file"
done

Split data using sed or awk

I have a lot of data I'm trying to split in CSV. My source data has this format:
* USER 'field1' 'mail1#domain.com' 'field3'
* USER 'field1' 'mail2#domain.com' 'field3'
* USER 'field1' 'mail3#domain.com' 'field3'
And here's what I'm trying to get as output:
field1;mail1#domain.com;field3
field1;mail2#domain.com;field3
field1;mail3#domain.com;field3
Rules:
* USER in the begin of the line must be obviously stripped;
field1 and field3 could be an email address, or can contain ';
field1 could be empty ''
the second field is always an email address;
each field has ' on the beginning and ending of the field itself.
My idea was to strip * USER (sed -e 's/^* USER //' could be a starting point), then "find" the mail in "the center" field, and then catch the left side and right side into two vars. Last thing should be to strip beginning and ending ' on the vars.
Unfortunately, I don't have sed or awk knowledge at this level. Any ideas on how to achieve this?
Here an example
* USER '' 'alberto.cordini#generaligroup.com' 'CORDINI ALBERTO'
* USER 'moglie delmonte daniele' 'anna.borghi#rpos.com' 'Anna Borghi'
* USER '' 'annamaria.cravero#generaligroup.com' 'CRAVERO ANNA MARIA'
* USER '' 'patrizia.dagostino#generaligroup.com' 'D'AGOSTINO PATRIZIA'
* USER '' 'piero.depra#generaligroup.com' 'DE PRA' PIERO'
* USER '' 'viviana.dingeo#generaligroup.com' 'D'INGEO VIVIANA'
Update: You can use this awk for the provided input:
awk -F " '" '{gsub(/^ +| +$/, "", $3);
s=sprintf("%s;%s;%s;", $2,$3,$4); gsub(/'"'"';/, ";", s); print s}' file
;alberto.cordini#generaligroup.com;CORDINI ALBERTO;
moglie delmonte daniele;anna.borghi#rpos.com;Anna Borghi;
;annamaria.cravero#generaligroup.com;CRAVERO ANNA MARIA;
;patrizia.dagostino#generaligroup.com;D'AGOSTINO PATRIZIA;
;piero.depra#generaligroup.com;DE PRA' PIERO;
;viviana.dingeo#generaligroup.com;D'INGEO VIVIANA;
Simply:
$ awk '{print $2,$4,$6}' FS="'" OFS=";" file
field1;mail1#domain.com;field3
field1;mail2#domain.com;field3
field1;mail3#domain.com;field3
You could use sed and awk and that would work but like you I don't use those often enough to remember (and I find them clunky). If you need a solution which you can put in a script to run all the time then how about a Ruby solution, I use a regular expression but you don't have to:
sample-data.txt
* USER 'field1' 'mail1#domain.com' 'field3'
* USER 'field1' 'mail2#domain.com' 'field3'
* USER 'field1' 'mail3#domain.com' 'field3'
parse.rb
#!/usr/bin/env ruby
$stdin.each_line do |e|
matches = e.match /\*\ USER\ '([\w]*)'\ '([\w\#\.]*)'\ '([\w]*)'/
if matches != nil
puts "#{matches[1]};#{matches[2]};#{matches[3]}"
end
end
From terminal/command-line:
cat sample-data.txt | ruby parse.rb
p.s. For me if it's a one-time-kind-of-problem, I would use Notepad++ in Windows. I would open the file, then record a macro, and play the macro to the end of the file, done.
sed "s/²/²S/g;s/\\'/²q/g;s/\*[[:blank:]]USER[[:blank:]]\{1,\}'\([^']*\)'[[:blank:]]*'\([^']*\)'[[:blank:]]*'\(.*\)'[[:blank:]]*$/\1;\2;\3/;s/²q/\\'/g;s/²S/²/g" YourFile.csv
Assuming there is no field 1 with ' inside that is/are not escaped
A sed example, it relies on the fact that there are single spaces between quote-delimited fields. If that's not the case then it needs modification to be more "flexible".
To avoid shell quote-escaping which is kind of ugly experience, I would put one liner into a file. -r makes it using extended regexp (avoid quoting ()s). Single quotes inside field1 and field3 are preserved by regexp greediness (eat everything, including quotes until last quote :)
sed -r -f s.sed samp.csv
s.sed:
s/\* USER '(.*)' '([^']*)' '(.*)'/\1;\2;\3/