how to deal with the special code such as # in the sed sentence - bash

I defined a marco switch in PublicDefine.h to control the target device in my code.
I meant to replace the target sentence automatically according to the options in my build shell.
Below is a peace of my code, I got an error "sed: -e expression #1, char 5: comments don't accept any addresses".
May somebody tell me how to deal with the #? Or you can just give me another suggestion. Thank you!
nLine=`grep NO_LCD PublicDefine.h -nR | cut -d ":" -f 1`
dfine_nolcd="#define NO_LCD 1"
ndfine_nolcd="#undef NO_LCD "
echo $dfine_nolcd #this is a debugging sentence
echo $ndfine_nolcd #this is a debugging sentence
echo "nLine $nLine"
if [ "$1"x = "NO_LCD"x ]; then
sed -i "${nLine} ${dfine_nolcd}" PublicDefine.h
else
sed -i "${nLine} ${ndfine_nolcd}" PublicDefine.h
fi

Okay, as you noticed, the problem you had was that you gave sed a line number and a string, but not a command. It took the # sign in your string as a command starting a comment.
But finding out the line number is still not necessary, as sed can match lines based on their contents instead of a line number, so you could just use something like these:
Change any line containing NO_LCD with a given string:
sed -e '/NO_LCD/c#define NO_LCD 1' PublicDefine.h
(/regex/ - on lines matching the regex, c - change line to following string.)
Or unconditionally try to do a string replacement over the full line:
sed -e 's/^.*NO_LCD.*$/#define NO_LCD 1/' PublicDefine.h
Assuming PublicDefine.h contains
something
NO_LCD replace me
something
both print
something
#define NO_LCD 1
something

sed -i '2 c xyz' file
can be used to replace the given line of file by "xyz".
The below sentence solved my problem:
sed -i "${nLine} c ${dfine_nolcd}" PublicDefine.h

Related

sed/awk look for pattern in string and change another pattern on the same line

I have an F5 bigip.conf text file in which I want to change the route domain from 701 to 703 for all lines showing "10.166.201." The route domain is represented by %701
10.166.201.10%701
10.166.201.15%701
10.166.201.117%701
I am able to do this with bash but the problem is that the "else printf" command (I've also tried echo), which is supposed to print out all other lines, incorrectly parses things like "\r\n" and leaves them as "rn"
#!/bin/bash
while read line
do
if [[ $line = *"10.166.201"* ]];
then
printf '%s\n' "$line" | sed -e 's/701/703/'
else printf '%s\n' "$line"
fi
done < bigip.conf > bigip.conf_updated
Is there a way to stop printf and echo from modifying the "\r\n"?
Is there a better way to do this in sed/awk?
Thanks.
Use a regexp address:
sed '/10\.166\.201\./s/%701/%703/' bigip.conf
Once you made sure that the command works for you, you can change the file in place using -i:
sed -i'' '/10\.166\.201\./s/%701/%703/' bigip.conf
With GNU sed you can omit the option value for -i:
sed -i '/10\.166\.201\./s/%701/%703/' bigip.conf
sed:
sed -E 's/(10\.166\.201\.[[:digit:]]+%70)1/\13/'
The captured group, (10\.166\.201\.[[:digit:]]+%70) matches 10.166.201. literally, then one or more digits, then %70 literally
Outside the captured group, 1 matches literally; in the replacement, the the captured group is used and 1 is replaced by 3
Example:
% cat file.txt
10.166.201.10%701
10.166.201.15%701
10.166.201.117%701
% sed -E 's/(10\.166\.201\.[[:digit:]]+%70)1/\13/' file.txt
10.166.201.10%703
10.166.201.15%703
10.166.201.117%703
Following awk may help you on same.
awk '{gsub(/\r/,"")} /your_string/{sub(/701/,"703")} 1' Input_file
Also in case you want to save output into same Input_file itself then do following:
awk '{gsub(/\r/,"")} /your_string/{sub(/701/,"703")} 1' Input_file > temp_file && mv temp_file Input_file
EDIT: In case your Input_file has \r in them then I added {gsub(/\r/,"")} in my above codes, in case you don't have them you could remove them from codes.
EDIT2: Changing string to your_string also change . to \. too in your address.
The clear, simple, robust, efficient way is:
awk 'BEGIN{RS=ORS="\r\n"; FS=OFS="%"} index($1,"10.166.201.")==1{ $2="703" } 1' file
Note that you don't need to escape the .s or anchor to avoid partial matches because the above simply treats the IP address as a string appearing at the start of the line. The above uses GNU awk for multi-char RS to preserve your \r\n line endings.

sed replace line with multiline-variable

I'm trying to replace a single line in a file with a multiline string stored in a variable.
I am able to get the correct result when printing to screen, but not if I want to do an in-place replacement.
The file has form:
*some code*
*some code*
string_to_replace
*some code*
I want the resulting file to be:
*some code*
*some code*
line number 1
line number 2
line number 3
*some code*
The code I tried was:
new_string="line number 1\nline number 2\nline number 3"
# Correct output on screen
sed -e s/"string_to_replace"/"${new_string}"/g $file
# Single-line output in file: "line number 1line number 2line number 3"
sed -i s/"string_to_replace"/"${new_string}"/g $file
When trying to combine -i and -e options, the result is the same as when only using -i.
I'm using GNU sed version 4.1.5 on CentOS (connected to it through ssh from Mac).
Although you have specifically asked for sed, you can accomplish this using awk by storing your multiline variable in a file using the following
awk '/string_to_replace/{system("cat file_with_multiple_lines");next}1' file_to_replace_in > output_file
In sed you can double quote the command string and let the shell do the expansion for you, like this:
new_string="line number 1\nline number 2\nline number 3"
sed -i "s/string_to_replace/$new_string/" file
Inlining a multi-line string into a sed script requires you to escape any literal newlines (and also any literal & characters, which otherwise interpolates the string you are replacing, as well as of course any literal backslashes, and whichever character you are using as the replacement delimiter). What exactly will work also depends slightly on the precise sed dialect. Ultimately, this may be one of those cases where using something else than sed is more robust and portable. But try e.g.
sed -e 's/[&%\\]/\\&/g' \
-e '$!s/$/\\/' \
-e '1s/^/s%string_to_replace%/' \
-e '$s/$/%g/' <<<$replacement |
# pass to second sed instance
sed -f - "$file"
The <<<"here string" syntax is Bash-specific; you can replace it with printf '%s\n' "$replacement" | sed.
Not all sed versions allow you to pass in a script on standard input with -f -. Maybe try replacing the lone dash with /dev/stdin or /dev/fd/0; if that doesn't work, either, you'll have to save the generated script to a temporary file. (Bash lets you use a command substitution sed -f <(sed ...) "$file" which can be quite convenient, and saves you from having to remove the temporary file when you are done.)
Demo: https://ideone.com/uMqqcx

Using variable with sed

First of all i apologise in case this has been answered before but i couldn't solve my problem.
I need to search a pattern and then replace it with a line of text comprising of both text and variable.Btw i am using bash..
say
$var = "stacko.ver/rulz=" **Note: $var contain double quotes & = & a dot and /**
i want to so the follow
1.Search for ;te.xt = Note: The value to be search contain ; & = and a dot
2.Replace it with
textnum=$var
Of course $var should be replaced with its actual value
My attempts
sed -i "s/;te.xt =/textnum=$var/" file
sed -i "s/;te.xt =/textnum="$var"/" file
sed -i "s/";te.xt ="/"textnum=$var"/" file
None of these actually worked , either sed giving me an error or the value of $var not shown in file
Thanks for the help
Regards
Quoting doesn't help since this is a sed issue, not a bash issue. Just pick a sed s-expression delimiter that doesn't appear in your text:
sed -i "s|;te.xt =|textnum=$var|" file
You can pick any delimiter for s that doesn't appear in your input. sed -e 'streetlight' is a perfectly valid sed command.
I can see the error:
$ var="stacko.ver/rulz="
$ data="foo ;te.xt = bar"
$ sed "s/;te.xt =/textnum=$var/" <<< "$data"
sed: -e expression #1, char 31: unknown option to `s'
The problem is that $var contains a slash, so sed's s/// command is breaking. You need to pick a character that does not appear in $var
$ sed "s#;te.xt =#textnum=$var#" <<< "$data"
foo textnum=stacko.ver/rulz= bar
This can be hard -- what if slash and hash are in $var? Using bash, you can use ANSI-C quoting to use a control character that is unlikely to appear in your data, e.g.
$ sed $'s\037;te.xt =\037textnum=$var\037' <<< "$data"
foo textnum=stacko.ver/rulz= bar

Get substring from file using "sed"

Can anyone help me to get substring using sed program?
I have a file with this line:
....
define("BASE", "empty"); # there can be any string (not only "empty").
....
And I need to get "empty" as string variable to my bash script.
At this moment I have:
sed -n '/define(\"BASE\"/p' path/to/file.ext
# returns this line:
# define("BASE", "empty");
# but I need 'empty'
UPD: Thanks to #Jaypal
For now I have bash script:
DBNAME=`sed -n '/define(\"BASE\"/p' path/to/file.ext`
echo $DBNAME | sed -r 's/.*"([a-zA-Z]+)".*/\1/'
It work OK, but if there any way to make the same manipulation with one line of code?
You should use is
sed -n 's/.*\".*\", \"\(.*\)\".*/\1/p' yourFile.txt
which means something (.*) followed by something in quotes (\".*\"), then a comma and a blank space (,), and then again something within quotes (\"\(.*\)\").
The brackets define the part that you later can reuse, i.e. the string within the second quotes. used it with \1.
I put -n front in order to answer the updated question, to get online the line that was manipulated.
This should help -
sed -r 's/.*"([a-zA-Z]+)"\);/\1/' path/to/file.ext
If you are ok with using awk then you can try the following -
awk -F\" '/define\(/{print $(NF-1)}' path/to/file.ext
Update:
DBNAME=$(sed -r '/define\(\"BASE\"/s/.*"([a-zA-Z]+)"\);/\1/' path/to/file.ext)
sed -nr '/^define.*"(.*)".*$/{s//\1/;p}' path/to/file.ext
if your file doesn't change over time (i.e. the line numbers will always be the same) you can take the line, and use delimiters to take your part out:
`sed -n 'Xp' your.file | cut -d ' ' -f 2 |cut -d "\"" -f 2`
assuming X is the line number of your required line

Delete all lines beginning with a # from a file

All of the lines with comments in a file begin with #. How can I delete all of the lines (and only those lines) which begin with #? Other lines containing #, but not at the beginning of the line should be ignored.
This can be done with a sed one-liner:
sed '/^#/d'
This says, "find all lines that start with # and delete them, leaving everything else."
I'm a little surprised nobody has suggested the most obvious solution:
grep -v '^#' filename
This solves the problem as stated.
But note that a common convention is for everything from a # to the end of a line to be treated as a comment:
sed 's/#.*$//' filename
though that treats, for example, a # character within a string literal as the beginning of a comment (which may or may not be relevant for your case) (and it leaves empty lines).
A line starting with arbitrary whitespace followed by # might also be treated as a comment:
grep -v '^ *#' filename
if whitespace is only spaces, or
grep -v '^[ ]#' filename
where the two spaces are actually a space followed by a literal tab character (type "control-v tab").
For all these commands, omit the filename argument to read from standard input (e.g., as part of a pipe).
The opposite of Raymond's solution:
sed -n '/^#/!p'
"don't print anything, except for lines that DON'T start with #"
you can directly edit your file with
sed -i '/^#/ d'
If you want also delete comment lines that start with some whitespace use
sed -i '/^\s*#/ d'
Usually, you want to keep the first line of your script, if it is a sha-bang, so sed should not delete lines starting with #!. also it should delete lines, that just contain only a hash but no text. put it all together:
sed -i '/^\s*\(#[^!].*\|#$\)/d'
To be conform with all sed variants you need to add a backup extension to the -i option:
sed -i.bak '/^\s*#/ d' $file
rm -Rf $file.bak
You can use the following for an awk solution -
awk '/^#/ {sub(/#.*/,"");getline;}1' inputfile
This answer builds upon the earlier answer by Keith.
egrep -v "^[[:blank:]]*#" should filter out comment lines.
egrep -v "^[[:blank:]]*(#|$)" should filter out both comments and empty lines, as is frequently useful.
For information about [:blank:] and other character classes, refer to https://en.wikipedia.org/wiki/Regular_expression#Character_classes.
If you want to delete from the file starting with a specific word, then do this:
grep -v '^pattern' currentFileName > newFileName && mv newFileName currentFileName
So we have removed all the lines starting with a pattern, writing the content into a new file, and then copy the content back into the source/current file.
You also might want to remove empty lines as well
sed -E '/(^$|^#)/d' inputfile
Delete all empty lines and also all lines starting with a # after any spaces:
sed -E '/^$|^\s*#/d' inputfile
For example, see the following 3 deleted lines (including just line numbers!):
1. # first comment
2.
3. # second comment
After testing the command above, you can use option -i to edit the input file in place.
Just this!
Here is it with a loop for all files with some extension:
ll -ltr *.filename_extension > list.lst
for i in $(cat list.lst | awk '{ print $8 }') # validate if it is the 8 column on ls
do
echo $i
sed -i '/^#/d' $i
done

Resources