sed replace end of specific string in file - bash

I have a hash file that takes the form of:
SHA1(disk.iso)= 43798473890473280573920473902472083947320
I need to replace the old hash with the new hash.
I've been trying to modify some old code with no luck:
sed -i 's/SHA1(disk.iso)"[^+]*"/"'" $HASH"'"/' manifest
Any thoughts here?
* UPDATE *
The sting listed above is correct:
SHA1(disk.iso)= (some SHA1 hash here. Note the space after the equal sign.)
Here is the current code:
sed -i "s/\(SHA1(disk.iso)=\).*/\1 $HASH/" manifest
but still nothing. This does not modify the line in question.
* SOLUTION *
THIS WORKS:
sed -i "s/\(SHA1(disk.iso)=\).*/\1 $HASH/" manifest
I just had the file name wrong. Thank you Janos

Here you go:
sed -i "s/\(SHA1(disk.iso)=\).*/\1 $HASH/" manifest
That is:
Capture the filename within \(...\), and match the rest of the line with .*
Replace the pattern (the entire line) with the captured filename \1, and append the $HASH
The whole thing within double quotes, so that shell variables are expanded.
Here's another variation to do the same thing:
sed -i "/^SHA1(disk.iso)/ s/=.*/= $HASH/" manifest
That is:
For lines starting with SHA1(disk.iso)
Replace the = sign and everything after it with = $HASH

You regular expression seems to be strange.
You use to many quotes.
You can just do (if you now the hashes):
sed -i "s/$OLDHASH/$NEWHASH" manifest
And if you don't know them and just want to replace any line with SHA1(disk.iso),
you can write:
sed -i "s/\(SHA1(disk.iso)=\).*/\1 $HASH/"
\(\) here mean backreferences; that means that you save the line in a register, that will be later used using \1. Of course, you could write directly:
sed -i "s/SHA1(disk.iso)=.*/SHA1(disk.iso) $HASH/"
but in this case it would be impossible to write something like disk[123].iso
to match several ISOs at once.

Two steps:
find the right line
replace the number in that line with a new number
Typically you do this with
cat myHashFile.txt | sed '/SHA1(disk.iso)/ {s/\d+/'$HASH'/}' > newHashFile.txt
The first term in /.../ in general takes a regular expression "apply what follows to lines meeting this condition"
The second part in {..} is the command:
s substitute
\d any digit
\d+ one or more digits (greedy)
$HASH replace with the contents of the $HASH variable

Related

Changing a line of text with sed with special characters

The name in the title says it all. However, I'm absolutely the worst with the sed command. So I'm trying to edit the following file:
/var/www/html/phpMyAdmin/config.inc.php
I want to edit the line that says
$cfg['Servers'][$i]['AllowRoot'] = false;
into the following
$cfg['Servers'][$i]['AllowRoot'] = true;
It has so many special characters and whatnot and I have no prior knowledge of how sed works. So here's some commands I've tried to specifically edit that one line.
sed -i "/*.AllowRoot.*/\$cfg['Servers'][\$i]['AllowRoot'] = true;/" /var/www/html/phpMyAdmin/config.inc.php
sed -i "/*.AllowRoot.*/$cfg['Servers'][$i]['AllowRoot'] = true;/" /var/www/html/phpMyAdmin/config.inc.php
# this one finds the line successfully and prints it so I know it's got the right string:
sed -n '/AllowRoot/p' /var/www/html/phpMyAdmin/config.inc.php
sed -i "s/'AllowRoot|false'/'AllowRoot|true'/" /var/www/html/phpMyAdmin/config.inc.php
I have absolutely no idea what I'm doing and I'm not learning a whole lot besides the feeling that the last command splits up 'AllowRoot|false' makes sure that both must be present in the sentence to come back as a result. So to my logic, I thought changing the word false into true would make that happen, but nothing. The other commands return... bizarre results at best, one even emptying the file. Or that's one of the commands I had not written down here, I've lost track after 50 attempts. What is the solution here?
The [ and ] need to be escaped to match literal brackets, instead of inadvertently starting a bracket expression. This should work:
$ sed -i "/\$cfg\['Servers'\]\[\$i\]\['AllowRoot'\]/s/false/true/" /var/www/html/phpMyAdmin/config.inc.php
There is not many things to escape in sed. Main problem in your line is / which you have chosen as delimiter (most common, but not required). I suggest you use # and the following will work:
sed -i "s#$cfg['Servers'][$i]['AllowRoot'] = false;<br />#$cfg['Servers'][$i]['AllowRoot'] = true;<br />#g" input.txt
however you need to think about bash interpreter as well. $i and $cfg will be interpreted as variables. My suggestion is that when you want to match a string like this to put the sed expression in a text file like this:
cat allow_root_true.sed
s#['Servers'][]['AllowRoot'] = false;<br />#['Servers'][]['AllowRoot'] = true;<br />#g
and run the command using sed -f like this:
sed -i -f allow_root_true.sed input.txt
Warning -i will change the input file
sed can't do literal string matching which is why you need to escape so many characters (see Is it possible to escape regex metacharacters reliably with sed), but awk can:
$ awk -v str="\$cfg['Servers'][\$i]['AllowRoot']" 'index($0,str){sub(/false/,"true")} 1' file
//some text here
$cfg['Servers'][$i]['AllowRoot'] = true;<br />
//some more text here
Run code snippetHide resultsExpand snippet
In the above we only have to escape the $s to protect them from the shell since the string is enclosed in "s to allow it to include 's.

Use sed to comment out .env entry

I'm writing a script that will comment and un-comment field in .env file.
sed -i "s/#SENDGRID_API_KEY/SENDGRID_API_KEY/g" $mlEnv
for uncomment
sed -i "s/[^#]SENDGRID_API_KEY/#SENDGRID_API_KEY/g" $mlEnv
for comment out, I use [^#] so that it will not add one more # when it is already commented
But second one doesnt work, although
grep "[^#]SENDGRID_API_KEY" $mlEnv
works ok.
You may use
sed -i -E 's/(^|[^#])(SENDGRID_API_KEY)/\1#\2/g' "$mlEnv"
Here, -E enables POSIX ERE regex syntax, (^|[^#]) captures (into Group 1) either start of string or any char but # in Group 1 and (SENDGRID_API_KEY) captures SENDGRID_API_KEY in Group 2.
The \1#\2 replacement pattern replaces with Group 1 contents + # + Group 2 contents.
Variables which specify a file name argument should generally be within double quotes.
This might work for you (GNU sed):
sed -Ei 's/^#?(SENDGRID_API_KEY)/#\1/' file
This will replace a line beginning #SENDGRID_API_KEY or SENDGRID_API_KEY with #SENDGRID_API_KEY.
Another try with sed similar to potong's:
sed 's/^[[:space:]]*#*[[:space:]]*\(SENDGRID_API_KEY\)/#\1/' file > newfile
The ^[[:space:]]*#*[[:space:]]*\(SENDGRID_API_KEY\) pattern matches any whitespace, 0+ hash chars, then again any whitespaces and then a SENDGRID_API_KEY word captured in Group 1. The replacement is # and then the captured value.

Remove prefix of each line in a file and output to another file using sed

I have a source code file in which comments are prefixed with // (ie. double slashes and an empty space), I want to convert the source code into a document so I tried to cat file.c and pipe it to sed, the thinking is to replace "double slash and a space" if a line starts with it, with empty string, but it looks like the slash has some special meaning in sed, so what's the best way of constructing the sed arguments?
Thanks!
If you want to remove the special meaning of / from sed then following may help you in same.
sed 's/^\/\/ //g' Input_file
So I am escaping / here by using \ before it, so it will be taken as a literal character rather than it's special meaning in code. Also if you are happy with above command's result then use -i to save the changes in Input_file itself. Hope this helps.
The slash only has meaning if you allow it.
sed 's#^// +##' < file.c

How to remove line matching specific pattern from a file

I know sed could be used to delete specific line from file:
sed -i "/pattern/d" file
While the pattern of my case includes slash, like /var/log,
So I know I need escape: sed -i "/\/tmp\/dir/d" file
However, for my case, the pattern is dynamic, should be a variable
in a shell file, so I have to convert the variable value to replace
"/" with "\\/", then got this:
sed -i "/^${pattern_variable//\\//\\\\\\/}$/d" file
My question is, is there any better implementation which is more readable or simpler? Not only sed, other utility is also acceptable. Is it possible to handle not only slash but also other various symbols, like backslash or # ()?
you can use char other than /:
sed "\#$varHasSlash#d"
example:
kent$ foo="b/c"
kent$ echo "a
ab/cd
e"|sed "\#$foo#d"
a
e

remove absolute path using sed command

I have file which contain following context like
abc...
include /home/user/file.txt'
some text
I need to remove include and also complete path after include.
I have used following command which remove include but did not remove path.
sed -i -r 's#include##g' 'filename'
I am also trying to understand above command but did not understand following thing ( copy paste from somewhere)
i - modify file change
r - read file
s- Need input
g - Need input
Try this,
$ sed '/^include /s/.*//g' file.txt
abc...
some text
It remove all the texts in a line which starts with include. s means substitute. so s/.*//g means replace all the texts with null.g means global. The substitution will be applied globally.
OR
$ sed '/^include /d' file.txt
abc...
some text
d means delete.
It deletes the line which starts with include. To save the changes made(inline edit), your commands should be
sed -i '/^include /s/.*//g' file.txt
sed -i '/^include /d' file.txt
I your case if you just want to delete the second line, you can use:
sed -i '2d' file
If you want to explore something about linux commands then man pages are there for you.
Just go to terminal and type:
man sed
as per your question, The above command without -i will show the file content on terminal by deleting the second line from the input file. However, the input file remains unchanged. To update the original file or to make the changes permanently in the source file, use the -i option.
-i[SUFFIX], --in-place[=SUFFIX] :
edit files in place (makes backup if extension supplied)
-r or --regexp-extended :
option is to use extended regular expressions in the script.
s/regexp/replacement/ :
Attempt to match regexp against the pattern space. If success‐
ful, replace that portion matched with replacement. The
replacement may contain the special character & to refer to that
portion of the pattern space which matched, and the special
escapes \1 through \9 to refer to the corresponding matching
sub-expressions in the regexp.
g G : Copy/append hold space to pattern space.
grep -v
This is not about learning sed, but as an alternative (and short) solution, there is:
grep -v '^include' filename_in
Or with output redirection:
grep -v '^include' filename_in > filename_out
-v option for grep inverts matching (hence printing non-matching lines).
For simple deletion that's what I'd use; if you have to modify your path after the include, stick with sed instead.
You can use awk to just delete the line:
awk '/^include/ {next}1' file
sed -i -r 's#include##g' 'filename'
-i: you directly modify the treated file, by default, sed read a file, modify the content via stdout (the original file stay the same).
-r: use of extended regular expression (and not reduce to POSIX limited one).This is not necessary in this case due to simple POSIX compliant action in action list (the s### string).
s#pattern#NewValue#: substitute in current line the pattern (Regular Expression) with "Newvalue" (that also use internal buffer or specific value). The traditionnal form is s/// but in this case, using / in path (pattern or new value) an alternate form is used to avoid to escape all / in pattern or new value
g: is an option of s### that specify change EVERY occurence and not the first (by default)
so here it replace ANY occurence of include by nothing (remove) directly into your file
As per the Avinash Raj solution you got what you want but you want some explaination about some parameter used in sed command
First one is
command: s for substitution
With the sed command the substitute command s changes all occurrences of the regular expression into a new value. A simple example is changing "my" in the "file1" to "yours" in the "file2" file:
sed s/my/yours/ file1 >file2
The character after the s is the delimiter. It is conventionally a slash, because this is what ed, more, and vi use. It can be anything you want, however. If you want to change a pathname that contains a slash - say /usr/local/bin to /common/bin - you could use the backslash to quote the slash:
sed 's/\/usr\/local\/bin/\/common\/bin/' <old >new
/g - Global replacement
Replace all matches, not just the first match.
If you tell it to change a word, it will only change the first occurrence of the word on a line. You may want to make the change on every word on the line instead of the first then add a g after the last delimiter and use the work-around:
Delete with d
Delete the pattern space; immediately start next cycle.
You can delete line by specifying the line number. like
sed '$d' filename.txt
It will remove last line of file
sed '2 d' file.txt
It will delete second line of file.
-i option
This option specifies that files are to be edited in-place. GNU sed does this by creating a temporary file and sending output to this file rather than to the standard output.
To modify file actully you can use -i option without it sed command repressent changes on stdout not actual file. You can take backup of original file before modification by using -i.bak option.
-r option
--regexp-extended
Use extended regular expressions rather than basic regular expressions. Extended regexps are those that egrep accepts; they can be clearer because they usually have less backslashes, but are a GNU extension and hence scripts that use them are not portable.

Resources