Regarding sed expression evaluation in shell - linux

Could someone explain the meaning of below sed statement?
sed -i "s/PS1\='\\\\u\#[^]]*:/PS1\='\\\\u\#\\\\H:/g" test

First of all, note that PS1 is the bash prompt. See How to: Change / Setup bash custom prompt (PS1) for more references.
sed -i "s/PS1\='\\\\u\#[^]]*:/PS1\='\\\\u\#\\\\H:/g" test
It looks for the text PS1\='\\u\#[^]]*: and replaces it with PS1\='\\u\#\\H: in test file.
sed 's/hello/bye/g' file is the basic sed command that looks for hello and replaces it with bye all along the file (g means "global", so it does every time it finds the text).
While this sed expression shows the result on stdout, if you want the result to update the file, you add the -i option instead.
Then, note that I mentioned that the text looked for is PS1\='\\u\#[^]]*:, while in the sed expression we see PS1\='\\\\u\#[^]]*:. That's why any \ has to be escaped... and the \ character is used to do so.
Regarding the specific pattern looked for:
PS1\='\\u\#[^]]*:
means text like
PS1='\\u\#`
+
any string until the character `]` is found
+
:
So it will match texts like PS1\='\\u\#[hello how are you]:.
It replaces them with PS1\='\\u\#\\H:.

Related

How to delete a string with the same word and consecutively increasing number in a text file using a shell script?

For example I have:
Hello1 :
Hello2 :
Hello3 :
How Could I delete all of these with a shell script. The number reaches up all the way to 1000.
sed -i '/^Hello[[:digit:]]\+\>/d' file.txt
Or, if you want to output to a different file:
sed '/^Hello[[:digit:]]\+\>/d' file.txt > newfile.txt
If you wish to delete all the lines that contain only Hello(number) : use below :
Sample Input in file
Hell
Hello1 :
No hello stuff here
Unjulating stuff
Hello2 :
Some sentence
Hello99 :
Script
sed -Ei '/^Hello[[:digit:]]+ :$/d' file
Sample Output in the modified file
Hell
No hello stuff here
Unjulating stuff
Some sentence
What happens above
Using the ^ in the pattern we check for the beginning of the line.
We check the pattern Hello(number) : using Hello[[:digit:]]+ :$. Note that I used -E to enable sed extended regular expressions so I need not escape the + ie (\+). Here [[:digit:]] is a class which contains
all the decimal digits and + is used to check if the pattern before it matches at least one time.
Check the end of the line using $
For a matched pattern, delete it line using the d option
I have also used the sed inplace edit option -i so that the changes
are directly saved to the file.
If you wish to change the a line the begins with Hello(number) : then use the below script
sed -Ei '/^Hello[[:digit:]]+ :/d' file
You might have notices that I just removed the $, so our pattern matches any line that starts with Hello(number) :
Hope this helps.

Change variable evaluation method in all script from $VAR_NAME to ${VAR_NAME}

We have couple of scripts where we want to replace variable evaluation method from $VAR_NAME to ${VAR_NAME}
This is required so that scripts will have uniform method for variable evaluation
I am thinking of using sed for the same, I wrote sample command which looks like follows,
echo "\$VAR_NAME" | sed 's/^$[_a-zA-Z0-9]*/${&}/g'
output for the same is
${$VAR_NAME}
Now i don't want $ inside {}, how can i remove it?
Any better suggestions for accomplishing this task?
EDIT
Following command works
echo "\$VAR_NAME" | sed -r 's/\$([_a-zA-Z]+)/${\1}/g'
EDIT1
I used following command to do replacement in script file
sed -i -r 's:\$([_a-zA-Z0-9]+):${\1}:g' <ScriptName>
Since the first part of your sed command searches for the $ and VAR_NAME, the whole $VAR_NAME part will be put inside the ${} wrapper.
You could search for the $ part with a lookbehind in your regular expression, so that you end up ending the sed call with /{&}/g as the $ will be to the left of your matched expression.
http://www.regular-expressions.info/lookaround.html
http://www.perlmonks.org/?node_id=518444
I don't think sed supports this kind of regular expression, but you can make a command that begins perl -pe instead. I believe the following perl command may do what you want.
perl -p -e 's/(?<=\$)[_a-zA-Z0-9]*/{$&}/g'
PCRE Regex to SED

How to remove line containing a specific string from file?

I have a file BookDB.txt which stores information in the following manner :
C++ for dummies:Jared:10.67:4:5
Java for dummies:David:10.45:3:6
PHP for dummies:Sarah:10.47:2:7
Assuming that during runtime, the scipt asks the user for the title he wants to delete. This is then stored in the TITLE variable. How do I then delete the line containing the string in question? I've tried the following command but to no avail :
sed '/$TITLE/' BookDB.txt >> /dev/null
You can for example do:
$ title="C++ for dummies"
$ sed -i "/$title/d" a
$ cat a
**Java for dummies**:David:10.45:3:6
**PHP for dummies**:Sarah:10.47:2:7
Note few things:
Double quotes in sed are needed to have you variable expanded. Otherwise, it will look for the fixed string "$title".
With -i you make in-place replacement, so that your file gets updated once sed has performed.
d is the way to indicate sed that you want to delete such matching line.
Your command should be,
sed "/$TITLE/d" file
To save the changes, you need to add -i inline edit parameter.
sed -i "/$TITLE/d" file
For variable expansion in sed, you need to put the code inside double quotes.

Replace whole line containing a string using Sed

I have a text file which has a particular line something like
sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext
I need to replace the whole line above with
This line is removed by the admin.
The search keyword is TEXT_TO_BE_REPLACED
I need to write a shell script for this. How can I achieve this using sed?
You can use the change command to replace the entire line, and the -i flag to make the changes in-place. For example, using GNU sed:
sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
You need to use wildcards (.*) before and after to replace the whole line:
sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
The Answer above:
sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
Works fine if the replacement string/line is not a variable.
The issue is that on Redhat 5 the \ after the c escapes the $. A double \\ did not work either (at least on Redhat 5).
Through hit and trial, I discovered that the \ after the c is redundant if your replacement string/line is only a single line. So I did not use \ after the c, used a variable as a single replacement line and it was joy.
The code would look something like:
sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo
Note the use of double quotes instead of single quotes.
The accepted answer did not work for me for several reasons:
my version of sed does not like -i with a zero length extension
the syntax of the c\ command is weird and I couldn't get it to work
I didn't realize some of my issues are coming from unescaped slashes
So here is the solution I came up with which I think should work for most cases:
function escape_slashes {
sed 's/\//\\\//g'
}
function change_line {
local OLD_LINE_PATTERN=$1; shift
local NEW_LINE=$1; shift
local FILE=$1
local NEW=$(echo "${NEW_LINE}" | escape_slashes)
# FIX: No space after the option i.
sed -i.bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
mv "${FILE}.bak" /tmp/
}
So the sample usage to fix the problem posed:
change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
All of the answers provided so far assume that you know something about the text to be replaced which makes sense, since that's what the OP asked. I'm providing an answer that assumes you know nothing about the text to be replaced and that there may be a separate line in the file with the same or similar content that you do not want to be replaced. Furthermore, I'm assuming you know the line number of the line to be replaced.
The following examples demonstrate the removing or changing of text by specific line numbers:
# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
# just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE
# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
for manipulation of config files
i came up with this solution inspired by skensell answer
configLine [searchPattern] [replaceLine] [filePath]
it will:
create the file if not exists
replace the whole line (all lines) where searchPattern matched
add replaceLine on the end of the file if pattern was not found
Function:
function configLine {
local OLD_LINE_PATTERN=$1; shift
local NEW_LINE=$1; shift
local FILE=$1
local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
touch "${FILE}"
sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
then
echo "${NEW_LINE}" >> "${FILE}"
fi
}
the crazy exit status magic comes from https://stackoverflow.com/a/12145797/1262663
In my makefile I use this:
#sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md
PS: DO NOT forget that the -i changes actually the text in the file... so if the pattern you defined as "Revision" will change, you will also change the pattern to replace.
Example output:
Abc-Project written by John Doe
Revision: 1190
So if you set the pattern "Revision: 1190" it's obviously not the same as you defined them as "Revision:" only...
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'
it works fine
To do this without relying on any GNUisms such as -i without a parameter or c without a linebreak:
sed '/TEXT_TO_BE_REPLACED/c\
This line is removed by the admin.
' infile > tmpfile && mv tmpfile infile
In this (POSIX compliant) form of the command
c\
text
text can consist of one or multiple lines, and linebreaks that should become part of the replacement have to be escaped:
c\
line1\
line2
s/x/y/
where s/x/y/ is a new sed command after the pattern space has been replaced by the two lines
line1
line2
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file
done
find_replace file contains 2 columns, c1 with pattern to match, c2 with replacement, the sed loop replaces each line conatining one of the pattern of variable 1
To replace whole line containing a specified string with the content of that line
Text file:
Row: 0 last_time_contacted=0, display_name=Mozart, _id=100, phonebook_bucket_alt=2
Row: 1 last_time_contacted=0, display_name=Bach, _id=101, phonebook_bucket_alt=2
Single string:
$ sed 's/.* display_name=\([[:alpha:]]\+\).*/\1/'
output:
100
101
Multiple strings delimited by white-space:
$ sed 's/.* display_name=\([[:alpha:]]\+\).* _id=\([[:digit:]]\+\).*/\1 \2/'
output:
Mozart 100
Bach 101
Adjust regex to meet your needs
[:alpha] and [:digit:]
are Character Classes and Bracket Expressions
This worked for me:
sed -i <extension> 's/.*<Line to be replaced>.*/<New line to be added>/'
An example is:
sed -i .bak -e '7s/.*version.*/ version = "4.33.0"/'
-i: The extension for the backup file after the replacement. In this case, it is .bak.
-e: The sed script. In this case, it is '7s/.*version.*/ version = "4.33.0"/'. If you want to use a sed file use the -f flag
s: The line number in the file to be replaced. In this case, it is 7s which means line 7.
Note:
If you want to do a recursive find and replace with sed then you can grep to the beginning of the command:
grep -rl --exclude-dir=<directory-to-exclude> --include=\*<Files to include> "<Line to be replaced>" ./ | sed -i <extension> 's/.*<Line to be replaced>.*/<New line to be added>/'
The question asks for solutions using sed, but if that's not a hard requirement then there is another option which might be a wiser choice.
The accepted answer suggests sed -i and describes it as replacing the file in-place, but -i doesn't really do that and instead does the equivalent of sed pattern file > tmp; mv tmp file, preserving ownership and modes. This is not ideal in many circumstances. In general I do not recommend running sed -i non-interactively as part of an automatic process--it's like setting a bomb with a fuse of an unknown length. Sooner or later it will blow up on someone.
To actually edit a file "in place" and replace a line matching a pattern with some other content you would be well served to use an actual text editor. This is how it's done with ed, the standard text editor.
printf '%s\n' '/TEXT_TO_BE_REPLACED/' d i 'This line is removed by the admin' . w q | \
ed -s /tmp/foo > /dev/null
Note that this only replaces the first matching line, which is what the question implied was wanted. This is a material difference from most of the other answers.
That disadvantage aside, there are some advantages to using ed over sed:
You can replace the match with one or multiple lines without any extra effort.
The replacement text can be arbitrarily complex without needing any escaping to protect it.
Most importantly, the original file is opened, modified, and saved. A copy is not made.
How it works
How it works:
printf will use its first argument as a format string and print each of its other arguments using that format, effectively meaning that each argument to printf becomes a line of output, which is all sent to ed on stdin.
The first line is a regex pattern match which causes ed to move its notion of "the current line" forward to the first line that matches (if there is no match the current line is set to the last line of the file).
The next is the d command which instructs ed to delete the entire current line.
After that is the i command which puts ed into insert mode;
after that all subsequent lines entered are written to the current line (or additional lines if there are any embedded newlines). This means you can expand a variable (e.g. "$foo") containing multiple lines here and it will insert all of them.
Insert mode ends when ed sees a line consisting of .
The w command writes the content of the file to disk, and
the q command quits.
The ed command is given the -s switch, putting it into silent mode so it doesn't echo any information as it runs,
the file to be edited is given as an argument to ed,
and, finally, stdout is thrown away to prevent the line matching the regex from being printed.
Some Unix-like systems may (inappropriately) ship without an ed installed, but may still ship with an ex; if so you can simply use it instead. If have vim but no ex or ed you can use vim -e instead. If you have only standard vi but no ex or ed, complain to your sysadmin.
It is as similar to above one..
sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
Below command is working for me. Which is working with variables
sed -i "/\<$E\>/c $D" "$B"
I very often use regex to extract data from files I just used that to replace the literal quote \" with // nothing :-)
cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed s/\"//g | cut -d, -f1 > list.txt

How to search and replace text in a file from a shell script?

I'm trying to write a shell script that does a search and replace inside a configuration file upon start-up.
The string we're trying to replace is:
include /etc/nginx/https.include;
and we want to replace it with a commented version:
#include /etc/nginx/https.include;
The file that contains the string that we want to replace is:
/etc/nginx/app-servers.include
I'm not a Linux guru and can't seem to find the command to do this.
perl -p -i -e 's%^(include /etc/nginx/https.include;)$%#$1%' /etc/nginx/ap-servers.include
If the line might not end in the ;, use instead:
perl -p -i -e 's%^(include /etc/nginx/https.include;.*)$%#$1%' /etc/nginx/ap-servers.include
If you want to preserve the original file, add a backup extension after -i:
perl -p -i.bak -e 's%^(include /etc/nginx/https.include;)$%#$1%' /etc/nginx/ap-servers.include
Now, explaining. The -p flag means replace in-place. All lines of the file will be fed to the expression, and the result will be used as replacement. The -i flag indicates the extension of the backup file. By using it without anything, you prevent generation of backups. The -e tells Perl to get the following parameter as an expression to be executed.
Now, the expression is s%something%other%. I use % instead of the more traditional / to avoid having to escape the slashes of the path. I use parenthesis in the expression and $1 in the substituted expression for safety -- if you change one, the other will follow. Thus, %#$1% is actually the second % of s, followed by the desired #, $1 indicating the pattern inside parenthesis, and the last % of s.
HTH. HAND.
sed -i 's/foo/bar/g' config.txt
This replaces all instances of foo (case insensitive) with bar in the file config.txt
Check out sed:
sed -i -r 's|^(include /etc/nginx/https.include;)$|#\1|' /etc/nginx/app-servers.include
-i means do the substitution in-place and -r means to use extended regular expressions.
cd pathname
for y in `ls *`;
do sed "s/ABCD/DCBA/g" $y > temp; mv temp $y;
done
This script shold replace string ABCD to DCBA in all the files in pathname

Resources