I'd like change macro value in C file with sed.
For example,
#define MY_MACRO VALUE1
with
#define MY_MACRO VALUE2
sed should search for known MY_MACRO name (can be regex) and set new value provided. Old macro value isn't known when calling sed.
Any help would be very much appreciated.
Thanks in advance,
Markus
You can match any value with .*: . matches any character, * means repeat zero or more times.
sed 's/#define MY_MACRO .*/#define MY_MACRO VALUE2/'
You can make it shorter with a capture group
sed 's/\(#define MY_MACRO \).*/\1VALUE2/'
If the # sign must be at the beginning of a line, you can add the ^ special character that matches at line beginnings:
sed 's/^\(#define MY_MACRO \).*/\1VALUE2/'
But it can get even more complicated if there's a variable number of spaces between the words etc.
Related
I'm trying to replace doxygen comment from a file with swift comments.
e.g: /// \param foo should became /// - Parameter foo: with foo
So far I have
gsed -i 's/\\param/\- Parameter/g' my_file or perl -pe 's/\\param/\- Parameter/g'
I'd like to replace the following word (foo) after my expression with word: (foo:)
I didn't manage to find a good expression for that. Ideally, something that work on Linux and macOS
In perl you can capture and put back the word with $1 (first parentheses).
s/\\param\s+(.+)/- Parameter $1:/g
.+ will capture the rest of that line. If that is something you don't want, and just want to capture the first word, you can use \S+ or \w+ or whatever other character class that matches the characters you want to capture, e.g. [a-z_-]+.
In sed it is probably \1.
Using sed
$ sed -E 's/\\(param)( [^ ]*)/- \u\1eter\2:/' input_file
/// - Parameter foo:
You could use pattern with a single capture group, and use that group with \1 in the replacement.
sed -E 's/\\param[[:space:]]+([^[:space:]]+)/- Parameter \1:/g' my_file
The pattern matches:
\\param Match \param
[[:space:]]+ Match 1+ spaces
([^[:space:]]+) Capture group 1, match 1+ non whitespace chars
Output
- Parameter foo:
If you validated the output, then you can change sed -E to sed -Ei to do the replacement.
In a file, a would like to replace all occurences of a dot within braces to be replaced by an underscore.
input
something.dots {test.test} foo.bar
another.line
expected output
something.dots {test_test} foo.bar
another.line
What would be the easiest way to achieve that?
You can choose the least ugly sed from the two options below:
$ cat file
something.dots {test.test} foo.bar {a.a} x
something.dots
$ sed 's|\({[^}]*\)\.\([^}]*}\)|\1_\2|g' file
something.dots {test_test} foo.bar {a_a} x
something.dots
$ sed -E 's|(\{[^}]*)\.([^}]*\})|\1_\2|g' file
something.dots {test_test} foo.bar {a_a} x
something.dots
Explanation (I'll use the last form, but they are equivalent):
(\{[^}]*): Matching group 1 consisting of a {, and any number of non-} characters.
\.: A dot.
([^}]*\}): Matching group 2 consisting of any number of non-} characters followed by a }.
If found, replace the whole expression by [Matching group 1].[Matching group 2].
easiest way
Hold the line, extract the part within braces, do the substitution, grab the holded line and shuffle it for the output.
sed 'h;s/.*{//;s/}.*//;s/\./_/g;G;s/^\(.*\)\n\(.*{\).*}/\2\1}/'
#edit - ignore lines without {.*}:
sed '/{.*}/!b; h;s/.*{//;s/}.*//;s/\./_/g;G;s/^\(.*\)\n\(.*{\).*}/\2\1}/'
Tested on repl.
If it's going to be the "easiest way" use AWK instead of sed and then:
awk -F"{|}" '$0 !~ /{.*}/{print($0)}; gsub("\.","_",$2) {print($1"{"$2"}"$3)}' file
This will replace any number of dots, e.g. {test.test.test} and lines without parentheses leaves unchanged.
Explanation:
-F"{|}" Sets the field separator to { or }
$0 !~ /{.*}/{print($0)}; Prints lines unchanged without the {. *}
pattern, "print" can be omitted as this is
the default behavior
gsub("\.","_",$2) Substitutions . to _ for field 2
{print($1"{"$2"}"$3)} Formats and prints lines after changes
C file has 100s of lines with strlcpy, I have to copy the first parameter and add as the a third argument for the strlpcy using - Eg p->account to sizeof(p->account)
Input
strlcpy(p->account,gettoken(NULL,&plast)); //Set Account Information
strlcpy(p->balance,gettoken(NULL,&plast));
strlcpy(p->startDate,skipchr(gettoken(NULL,&plast),'0')); /* YYYYMMDD */
strlcpy(p->endDate,skipchr(gettoken(NULL,&plast),'0')); /* YYYYMMDD */
strlcpy(p->status,gettoken(NULL,&plast));
Expected Output (Copy the first parameter and add as a third argument - pass as parameter for sizeof ());
strlcpy(p->account,gettoken(NULL,&plast),sizeof(p->account)); //Set Account Information
strlcpy(p->balance,gettoken(NULL,&plast),sizeof(p->balance));
strlcpy(p->startDate,skipchr(gettoken(NULL,&plast),'0'),sizeof(p->startDate)); /* YYYYMMDD */
strlcpy(p->endDate,skipchr(gettoken(NULL,&plast),'0'),sizeof(p->endDate)); /* YYYYMMDD */
strlcpy(p->status,gettoken(NULL,&plast),sizeof(p->status));
Current Ouput (Incorrect Result)
sed 's/^\([^\s]*strlcpy[^(]*\)\(([^,]*\),\([^)]*[^)][^;]\).*/\1\2,\3,sizeof\2));/' kkk1.txt
strlcpy(p->account,gettoken(NULL,&plast),sizeof(p->account));
strlcpy(p->balance,gettoken(NULL,&plast),sizeof(p->balance));
strlcpy(p->startDate,skipchr(gettoken(NULL,&plast),sizeof(p->startDate));
strlcpy(p->endDate,skipchr(gettoken(NULL,&plast),sizeof(p->endDate));
strlcpy(p->status,gettoken(NULL,&plast),sizeof(p->status));
Line1, 3, and 4 Failed to Print the comments at the end of line
Line 3 and 4 : skipchr(gettoken(NULL,&plast),'0') - Parameter '0' failed to get copied in the result along with skipchr() in the result.
Guide me with the correct sed command. Thanks in Advnace
In general, such a task is beyond the power of sed. That's because C has an (approximately) context-free grammar, which cannot be parsed using only regular expressions. Regular grammars cannot describe arbitrarily nested parentheses, for example.
However, if you have sufficient constraints on your inputs, you can use sed for your specific, constrained, source code. In this case, it appears that we can make these assumptions:
Each relevant statement is on a single line
There is only one statement on each relevant line.
The first argument contains no commas.
There are no string literals or comments that might accidentally match.
Given these constraints, we want to match:
The strlcpy name and opening parenthesis (with optional space between): \bstrlcpy\s*(
The first argument, up to the first comma: [^,]+
The rest of the arguments: ,.+
The final closing parenthesis and semicolon: )\s*;
We then want to substitute this with the same text, except that we want sizeof group 2 interposed between groups 3 and 4 in the replacement: \1\2\3, sizeof \2\4.
Putting this together into a GNU sed one-liner:
#!/bin/sed -rf
s/\b(strlcpy\s*\()([^,]+)(,.+)(\)\s*;)/\1\2\3, sizeof \2\4/
Feeding your sample code through this gives the desired output:
strlcpy(p->account,gettoken(NULL,&plast), sizeof p->account); //Set Account Information
strlcpy(p->balance,gettoken(NULL,&plast), sizeof p->balance);
strlcpy(p->startDate,skipchr(gettoken(NULL,&plast),'0'), sizeof p->startDate); /* YYYYMMDD */
strlcpy(p->endDate,skipchr(gettoken(NULL,&plast),'0'), sizeof p->endDate); /* YYYYMMDD */
strlcpy(p->status,gettoken(NULL,&plast), sizeof p->status);
(Note: I didn't include the unnecessary parentheses around the argument to sizeof, as that makes it look like applying sizeof to a type rather than to an expression; if you feel very strongly that you want them, it's not difficult to do. But I don't encourage it.)
$ str="strlcpy(p->account,gettoken(NULL,&plast));"
$ sed -re '/strlcpy/{s/(\([^,]*)([^\)]*\))/\1\2,sizeof\1\)/}' <<< "$str"
strlcpy(p->account,gettoken(NULL,&plast),sizeof(p->account));
Here's the brief explanation,
/strlcpy/: find the line matched "strcpy", and use behind script to process it
(...): \1 and \2 refer to the corresponding matching regex between parentheses (...). Mind that because of the -r parameter, no need to escape the parentheses.
The regex pattern which contains parentheses, they need to be escaped as \(
The final step to edit the files in place, add -i option to do that.
Try this:
sed 's/^\(\s*strlcpy(\)\([^,]\+\)\(,.*\)\();.*\)$/\1\2\3,sizeof(\2)\4/'
For exmaple:
sed 's#/lib\(64\)\?\(32\)\?/ld#/tools&#g' abc.txt
What are those meanings such as 's#' , '(' , '\?' ,'&' ,and '#' ?
How to find a proper and simplest way to learn sed?
abc.txt:
#define GNU_USER_LINK_EMULATION32 "elf32%{EB:b}%{EL:l}tsmip"
#define GNU_USER_LINK_EMULATION64 "elf64%{EB:b}%{EL:l}tsmip"
#define GNU_USER_LINK_EMULATIONN32 "elf32%{EB:b}%{EL:l}tsmipn32"
#define GLIBC_DYNAMIC_LINKER32 "/lib/ld.so.1"
#define GLIBC_DYNAMIC_LINKER64 "/lib64/ld.so.1"
#define GLIBC_DYNAMIC_LINKERN32 "/lib32/ld.so.1"
#define UCLIBC_DYNAMIC_LINKERN32 "/lib32/ld-uClibc.so.0"
#define BIONIC_DYNAMIC_LINKERN32 "/system/bin/linker32"
#define GNU_USER_DYNAMIC_LINKERN32 \
CHOOSE_DYNAMIC_LINKER (GLIBC_DYNAMIC_LINKERN32, UCLIBC_DYNAMIC_LINKERN32, \
BIONIC_DYNAMIC_LINKERN32)
Thank you~
The manual is a good place to start http://www.gnu.org/software/sed/manual/sed.html
Breaking down that specific command:
sed 's#/lib\(64\)\?\(32\)\?/ld#/tools&#g' abc.txt
The string in quotes 's#/lib\(64\)\?\(32\)\?/ld#/tools&#g' is a single argument command for sed to run, it is the 's' ('substitute') command in this case http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
The separator used there is the # symbol, so the regexp is /lib\(64\)\?\(32\)\?/ld and the replacement is /tools&. There is a 'g' option at the end, which applies to all matches within each line, not just the first.
For the regular expression, \( and \) delimit a capturing group, \? means "optionally match the previous atom (match zero or one times)" (the previous atom being that group). The rest of the characters are matched as is, so this means it would match /lib/ld, /lib64/ld, /lib32/ld or /lib6432/ld.
In the replacement, the & includes the whole match, so effectively it prefixes all of the matches with /tools.
you need to learn REGEX first before learning sed or awk. they are mostly regex based .
Start from here REGEX
Then you can start sed from here http://www.grymoire.com/Unix/Sed.html
The best way is learn by practice . After learning regex start looking at sed questions from SO and see how people are answering. Firstly try to solve yourself and then see the answer given
I have a file of string records where one of the fields - delimited by "," - can contain one or more "-" inside it.
The goal is to delete the field value if it contains more than two "-".
i am trying to recoup my past knowledge of sed/awk but can't make much headway
==========
info,whitepaper,Data-Centers,yes-the-6-top-problems-in-your-data-center-lane
info,whitepaper,Data-Centers,the-evolution-center
info,whitepaper,Data-Centers,the-evolution-of-lan-technology-lanner
==========
expected outcome:
info,whitepaper,Data-Centers
info,whitepaper,Data-Centers,the-evolution-center
info,whitepaper,Data-Centers
thanks
Try
sed -r 's/(^|,)([^,-]+-){3,}[^,]+(,|$)/\3/g'
or if you're into slashes
sed 's/\(^\|,\)\([^,-]\+-\)\{3,\}[^,]\+\(,\|$\)/\3/g'
Explanation:
I'm using the most basic sed command: substitution. The syntax is: s/pattern/replacement/flags.
Here pattern is (^|,)([^,-]+-){3,}[^,]+(,|$), replacement is \3, flags is g.
The g flag means global replacement (all matching parts are replaced, not only the first in line).
In pattern:
brackets () create a group. Somewhat like in math. They also allow to refer to a group with a number later.
^ and $ mean beginning and end of the string.
| means "or", so (^|,) means "comma or beginning of the string".
square brackets [] mean a character class, ^ inside means negation. So [^,-] means "anything but comma or hyphen". Not that usually the hyphen has a special meaning in character classes: [a-z] means all lowercase letters. But here it's just a hyphen because it's not in the middle.
+ after an expression means "match it 1 or more times" (like * means match it 0 or more times).
{N} means "match it exactly N times. {N,M} is "from N to M times". {3,} means "three times or more". + is equivalent to {1,}.
So this is it. The replacement is just \3. This refers to the third group in (), in this case (,|$). This will be the only thing left after the substitution.
P.S. the -r option just changes what characters need to be escaped: without it all of ()-{}| are treated as regular chars unless you escape them with \. Conversely, to match literal ( with -r option you'll need to escape it.
P.P.S. Here's a reference for sed. man sed is your friend as well.
Let me know if you have further questions.
You could try perl instead of sed or awk:
perl -F, -lane 'print join ",", grep { !/-.*-.*-/ } #F' < file.txt
This might work for you:
sed 's/,\{,1\}[^,-]*\(-[^,]*\)\{3,\}//g file
sed 's/\(^\|,\)\([^,]*-\)\{3\}[^,]*\(,\|$\)//g'
This should work in more cases:
sed 's/,$/\n/g;s/\(^\|,\|\n\)\([^,\n]*-\)\{3\}[^,\n]*\(,\|\n\|$\)/\3/g;s/,$//;s/\n/,/g'