sed: modify a file using contents of another file - bash

I've got a script which writes an ip address to a file ip.txt
I want to replace an ip address in an html file with the ip from ip.txt.
I've got a sed regex expression that matches an ip address, and I want to replace this matched text with the contents of ip.txt:
"s/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}//g"
How can I get sed to pull the contents of ip.txt and put it in the expression s/<search>/<*HERE*>/g?
Is there a better way to do it than this?
sed -e "s/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/"`cat ip.txt`"/g"

Not much of an improvement, but you can replace
"`cat ip.txt`"
with
$(<ip_txt)
which will be replaced with the contents of the file and is slightly more efficient than using cat.

You can read the IP into a shell variable before running the sed command. Assuming that ip.txt is a single line containing only the IP address:
read -r ip < ip.txt
sed -e "s/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/$ip/g" file.html > newfile.html
mv newfile.html file.html

POSIX sed (and therefore most of the available implementations of sed) supports the r file command to read a file when a line is matched. As long as you don't mind have the result containing newlines either side of where the IP address was, you could easily enough do it using r. The description says:
[1addr]r rfile
Copy the contents of rfile to standard output as described previously. If rfile does not exist or cannot be read, it shall be treated as if it were an empty file, causing no error condition.
This means that you don't get a chance to edit the contents of the file, whereas if it was read into the pattern space or hold space, you could could then modify the data.
This being the case, your command line substitution is about as good as you can do.

Related

grep exact pattern from a file in bash

I have the following IP addresses in a file
3.3.3.1
3.3.3.11
3.3.3.111
I am using this file as input file to another program. In that program it will grep each IP address. But when I grep the contents I am getting some wrong outputs.
like
cat testfile | grep -o 3.3.3.1
but I am getting output like
3.3.3.1
3.3.3.1
3.3.3.1
I just want to get the exact output. How can I do that with grep?
Use the following command:
grep -owF "3.3.3.1" tesfile
-o returns the match only and not the whole line.-w greps for whole words, meaning the match must be enclosed in non word chars like <space>, <tab>, ,, ; the start or the end of the line etc. It prevents grep from matching 3.3.3.1 out of 3.3.3.111.
-F greps for fixed strings instead of patterns. This prevents the . in the IP address to be interpreted as any char, meaning grep will not match 3a3b3c1 (or something like this).
To match whole words only, use grep -ow 3.3.3.1 testfile
UPDATE: Use the solution provided by hek2mgl as it is more robust.
You may use anhcors.
grep '^3\.3\.3\.1$' file
Since by default grep uses regex, you need to escape the dots in-order to make grep to match literal dot character.

Change specific part of a line in a file with sed

I have a line in a autoexec.py file that I want another script to be able to modify:
kodi.executebuiltin("PlayMedia(/path/to/file)")
I was thinking of using sed to override the value within PlayMedia() depending on certain conditions.
Can sed be used to only touch and overwrite the parts between the PlayMedia() brackets and nothing else? Or is further processing/regex needed?
You could use sed. The below code will replace the chars present inside the brackets following PlayMedia string with foo.
sed 's/\b\(PlayMedia\)([^)]*)/\1(foo)/g' file
If the string you want to replace is a path, you must use a different sed delimiter because filepath may contain forward slashes.
sed 's~\b\(PlayMedia\)([^)]*)~\1(foo)~g' file
Example:
$ echo 'kodi.executebuiltin("PlayMedia(/path/to/file)")' | sed 's/\b\(PlayMedia\)([^)]*)/\1(foo)/g'
kodi.executebuiltin("PlayMedia(foo)")

Delete unknown amount of regexps using sed

I'm trying to get a bunch of regular expressions for a file (one per line) and then fit those regexps into something like this /$regexp/d . I'm trying it this way:
while read line;do sed "/$line/d" to_delete.file >> output;done < to_delete.txt
But it says me 'unknown command', even if I change the delimiter.
--- EDIT
The to_delete.txt file has slashes but i'm already scraping them and that's where i find the error.
To avoid problem with / in regex sed is allow to use another separator, so you can use e.g. sed "\|$line|d".
Secondary if you put script into double-quotes you shoud add space between address range and action e.g. "\|$line| d"
But I see a general mistake in the script. The loop will print into output all to_delete.file (exept 1 line with regexp) by each loop. I suppose it is not the thing what OP wants.
If you'd like to exclude content of to_delete.txt from to_delete.file it can be easy done by grep
grep -vFf "to_delete.txt" "to_delete.file" > output

sed delete lines from a logfile that respect numbers in another file

I have a logfile that is starting to grow in size, and I need to remove certain lines that match a given pattern from it. I used grep -nr for extracting the target lines and copied them in a temp file, but I can't figure how can I tell sed to delete those lines from the log file.
I have found something similar here: Delete line from text file with line numbers from another file but this doesn't actually delete the lines, it only prints the wanted output.
Can anyone give me a hint?
Thank you!
I think, what you really need is sed -i '/pattern/d' filename.
But to answer your question:
How to delete lines matching the line numbers from another file:
(Assuming that there are no special characters in the line_numbers file, just numbers one per line...)
awk 'NR==FNR{a[$0]=1; next}; !(FNR in a)' line_numbers input.log
If you already have a way of printing what you want to standard output, there's no reason why you can't just overwrite the original file. For example, to only print lines that don't match a pattern, you could use:
grep -v 'pattern' original > tmp && mv tmp original
This redirects the output of the grep command to a temporary file, then overwrites the original file. Any other solution that does this "in-place" is only pretending to do so, after all.
There are numerous other ways to do this, using sed as suggested in the comments, or awk:
awk '!/pattern/' original > tmp && mv tmp original
If you want to use sed and your file is growing continuously, then you will have to execute sed -i '/REGEX/d' FILENAME more frequently.
Instead, you can make use of syslog-ng. You just have to edit the /etc/syslog-ng/syslog-ng.conf, wherein you need to create/edit an appropriate filter (somewhat like: f_example { not match(REGEX); }; ), save file, restart the service and you're done.
The messages containing that particular pattern will not be dumped in the log file. In this way, your file would not only stop growing, but also you need not process it periodically using sed or grep.
Reference
To remove a line with sed, you can do:
sed "${line}d" <originalLogF >tmpF
If you want remove several lines, you can pass a sed script. Here I delete the first and the second lines:
sed '1d;2d' <originalLogF >tmpF
If your log file is big, you probably have two pass. The first one to generate the sed script in a file, and a second one to apply the sed script. But it will be more efficient to have only one pass if you be able to recognize the pattern directly (and do not use "${line}d" at all). See Tom Fenech or anishsane answers, I think it is what you really need.
By the way you have to preserve the inode (not only the file name) because most of logger keep the file opened. So the final command (if you don't use sed -i) should be:
cat tmpF >originalLogF
By the way, the "-i" option (sed) is NOT magic, sed will create a temporary buffer, so if we have concurrent append to the log file, you can loose some lines.

Bash - find a keyword in a file and delete its line [duplicate]

This question already has answers here:
How to delete from a text file, all lines that contain a specific string?
(21 answers)
Closed 9 years ago.
I'd like to give a keyword, find the line where this keyword aṕpears in a file and erase the entire line.
This is what I got but it is not working as it should:
KEYWORD='domain.com'
cat /etc/hosts | grep -v "$KEYWORD" > /etc/hosts
UPDATE
This is what worked for me:
sed -i_bak -e '/domain\.com/d' /etc/hosts
However, as I had two lines with "domain.com", is there a way to tell sed to erase only the line where the exact keyword "domain.com" appears
This is the original content of /etc/hosts:
127.0.0.1 localhost localhost.localdomain
222.111.22.222 hvn.domain.com
222.111.22.222 domain.com
Here's how it end up after the command sed -i_bak -e '/domain\.com/d' /etc/hosts:
127.0.0.1 localhost localhost.localdomain
I tried sed -i_bak -e '/\<namourphoto\.com\.br\>/d' ~/Desktop/hosts but it didn't work.
CONCLUSION
This is the code I came up with (based on the help the fine folks have given me):
D=domain.com
DOMAIN=`echo "$D" | sed 's/\./\\\\./g'`
sed -i_bak -e "/[\t]$DOMAIN/d" /etc/hosts
Note that:
I am counting that there is always a tab before the domain to be erased
I am automatically escaping the dots of the domain name.
Use the stream editor, sed:
sed -i ".bak" '/culpa/d' test.txt
The above will delete lines containing culpa in test.txt. It will create a backup of the original (named test.txt.bak) and will modify the original file in-place.
Pipe it to another file, not the same one that you're reading from, and be careful with the useless use of cat.
grep -v "$KEYWORD" /etc/hosts > newfile
Apart from the fine answer given regarding sed, you can also use Perl and a little improved regex to solve this:
perl -pi.old -e 's/.*\sdomain\.com\s*\n//' file
Notice I'm considering domain.com will be isolated by space characters (tabs or spaces, and so on), and that nothing but zero or more spaces will appear after it until the newline. Similarly to -i in sed,
-i.old in perl sets the $^I variable, and your original file will receive the changes while a copy will be kept under the old name and a .old appended.
If you want to only delete last line of your example file
sed -i '/[[:space:]]\+domain\.com/d' test.txt

Resources