Sed command with exact variable change - linux

I want to replace exact word by sed command with variable. My file looks like this:
//module xyz
module xyz
Suppose I have the following shell variables defined:
var1='module xyz'
var2='module abc'
I want to change xyz to abc in uncommented line only(module xyz)
So after executing command output should be
//module xyz
module abc
I do not want to change commented line (//module xyz)
currently I am using sed command as,
sed -i "s|$var1|$var2|g" file_name
But this command doesn't work. It also replace commented line. How can I only replace the line that isn't commented?

Assuming that you know the pattern is at the start of the line, you can use this:
sed "s|^$var1|$var2|" file_name
That is, add an anchor ^, so that the match has to be at the start of the line.
I removed the -i switch so you can test it and also the g modifier, which isn't necessary as you only want to do one substitution per line.
It's worth mentioning that using shell variables in sed is actually quite tricky to do in a reliable way, so you should take this into account.

Your shell variable assignment should be quoted if there is space. Like:
var1="foo bar blah"
You can add pattern, "the lines don't start with // " to your sed command, so that do your substitution only for those lines
This line should work for your example:
sed -i "\#^//#\!s/$var1/$var2/g" file
the ! needs to be escaped, because we used double quote
since your pattern (comment) has slash (/), I used other char as regex separator
This command will only do substitution on lines not starting with //. If there are leading spaces, you may want to adjust the pattern ^//

You need to identify a pattern so that lines containing that pattern should not be processed.
Assuming that // will exist only in commented lines you can use
sed -i '/\/\// !s/$var1/$var2/g' file_name
/\/\// will enable sed to identify lines which contain the pattern //, and !s will enable you to skip those lines.

Related

How to add character at the end of specific line in UNIX/LINUX?

Here is my input file. I want to add a character ":" into the end of lines that have ">" at the beginning of the line. I tried seq -i 's|$|:|' input.txt but ":" was added to all the ending of each line. It is also hard to call out specific line numbers because, in each of my input files, the line contains">" present in different line numbers. I want to run a loop for multiple files so it is useless.
>Pas_pyrG_2
AAAGTCACAATGGTTAAAATGGATCCTTATATTAATGTCGATCCAGGGACAATGAGCCCA
TTCCAGCATGGTGAAGTTTTTGTTACCGAAGATGGTGCAGAAACAGATCTGGATCTGGGT
>Pas_rpoB_4
CAAACTCACTATGGTCGTGTTTGTCCAATTGAAACTCCTGAAGGTCCAAACATTGGTTTG
ATCAACTCGCTTTCTGTATACGCAAAAGCGAATGACTTCGGTTTCTTGGAAACTCCATAC
CGCAAAGTTGTAGATGGTCGTGTAACTGATGATGTTGAATATTTATCTGCAATTGAAGAA
>Pas_cpn60_2
ATGAACCCAATGGATTTAAAACGCGGTATCGACATTGCAGTAAAAACTGTAGTTGAAAAT
ATCCGTTCTATTGCTAAACCAGCTGATGATTTCAAAGCAATTGAACAAGTAGGTTCAATC
TCTGCTAACTCTGATACTACTGTTGGTAAACTTATTGCTCAAGCAATGGAAAAAGTAGGT
AAAGAAGGCGTAATCACTGTAGAAGAAGGCTCAGGCTTCGAAGACGCATTAGACGTTGTA
Here is experted output file:
>Pas_pyrG_2:
AAAGTCACAATGGTTAAAATGGATCCTTATATTAATGTCGATCCAGGGACAATGAGCCCA
TTCCAGCATGGTGAAGTTTTTGTTACCGAAGATGGTGCAGAAACAGATCTGGATCTGGGT
>Pas_rpoB_4:
CAAACTCACTATGGTCGTGTTTGTCCAATTGAAACTCCTGAAGGTCCAAACATTGGTTTG
ATCAACTCGCTTTCTGTATACGCAAAAGCGAATGACTTCGGTTTCTTGGAAACTCCATAC
CGCAAAGTTGTAGATGGTCGTGTAACTGATGATGTTGAATATTTATCTGCAATTGAAGAA
>Pas_cpn60_2:
ATGAACCCAATGGATTTAAAACGCGGTATCGACATTGCAGTAAAAACTGTAGTTGAAAAT
ATCCGTTCTATTGCTAAACCAGCTGATGATTTCAAAGCAATTGAACAAGTAGGTTCAATC
TCTGCTAACTCTGATACTACTGTTGGTAAACTTATTGCTCAAGCAATGGAAAAAGTAGGT
AAAGAAGGCGTAATCACTGTAGAAGAAGGCTCAGGCTTCGAAGACGCATTAGACGTTGTA
Do seq have more option to modify or the other commands can solve this problem?
sed -i '/^>/ s/$/:/' input.txt
Search the lines of input for lines that match ^> (regex for "starts with the > character). Those that do substitute : for end-of-line (you got this part right).
/ slashes are the standard separator character in sed. If you wish to use different characters, be sure to pass -e or s|$|:| probably won't work. Since / characters, unlike | characters, are not meaningful character within the shell, it's best to use them unless the pattern also contains slashes, in which case things get unwieldy.
Be careful with sed -i. Make a backup - make sure you know what's changing by using diff to compare the files.
On OSX -i requires an argument.
Using ed to edit the file:
printf "%s\n" 'g/^>/s/$/:/' w | ed -s input.txt
For every line starting with >, add a colon to the end, and then write the changed file back to disk.

OSX Terminal: how to add same item to each line of a csv file?

I have got a csv file like this:
120,256,300
36,255,12
etc...
I want to add a fixed string like 'USA' to all lines in order to obtain:
120,256,300,USA
36,255,12,USA
etc...
How can I do that?
Thanks
From a text processing point of view that CSV file is plain text in this context, you just want to attach , USA to each line.
The easiest (and operationally least expensive) way to do so is probably:
sed -i '' 's/$/, USA/' file
What this does is to instruct sed to look for the end of line $ and "replace" it with , USA. As sed is line-based this obviously doesn't actually trim out the new line of the file.
-i '' instructs sed to make the changes in-line without creating a backup file.
If you wanted a backup you can put the desired extension instead of '', e.g. -i .bak.
You can just use sed: cat <input-file> | sed 's/\(.*\)/\1, USA/'.
Here s is the substitute command, which uses the following character as a separator between a regular expression and a substitution. For the regular expression, the escaped parenthesis are used to create a capture group, the regex .* captures the entire line. For the substitution, the \1 inserts the first capture group, and then the , USA text is appended.
You can perform the replacement in place using: sed -i .bak 's/\(.*\)/\1, USA/' <input-file>

Replace first sign in line (linux bash)

I want to delete first sign in a file (without creating new file). That is the line (and this line isn't the first one or last one):
#$config['rrdcached'] = "unix:/var/run/rrdcached.sock";
I'm trying to do thuis with sed command but it doesn't work. That is my command:
sed -i "s/#$config\['rrdcached'\].*$/$config\['config'\]/g" text.txt
Any suggestions?
Just replace the first match of # with following command:
sed -i '1 s/#//' test.txt
The $ characters are causing two problems.
First, the shell is treating $config as a variable reference, and replacing it with the value. You need to escape the $ to prevent that.
Second, $ has special meaning in regular expressions, so you need to escape it at that level as well. So you need to escape the backslash and the $.
sed -i "s/^#\\\$config\['rrdcached'\].*\$/\$config['config']/" text.txt
There's no need for the g modifier since you only want to replace the first match on the line. And you should use the ^ anchor so it only matches this at the beginning of the line.
It's also not necessary to escape special regexp characters in the replacement string.
This command works, but i forgot that there is something after '='. At now, everything after that is deleted.
I wrote this:
sed -i "s/^#\\\$config\['rrdcached'\] = "unix:/var/run/rrdcached.sock";.*\$/\\\$config\['config'\]/ = "unix:/var/run/rrdcached.sock";" text.txt

Replacing string having forward slash in sed

I wish to replace
x.y.z.zz=/a/b/c/d/
with
x.y.z.zz=/a/b/e/d/
i know x.y.z.zz in advance.I also know the line number in advance.
I have tried this
sed "11s/.*/x.y.z.zz=\/a\/b\/e\/d\/" filename
but this is giving error. Is there a better way to directly search and replace the string ?
sed replaces by using the sed 's/pattern/replacement/' syntax. In your case, you were missing the last /. So by saying this it will work:
sed '11s/.*/x.y.z.zz=\/a\/b\/e\/d\//' file
^
However, it may be cleaner to use another delimiter, so that the syntax is more clear. What about #? (It can also be ~, _, etc.):
sed '11s#.*#x.y.z.zz=/a/b/e/d/#' file
Test
$ cat a
a
x.y.z.zz=/a/b/c/d/
b
c
Let's replace line 2:
$ sed '2s#.*#x.y.z.zz=/a/b/e/d/#' a
a
x.y.z.zz=/a/b/e/d/
b
c
You can just replace c with e if you know your input will always have "x.y.z.zz=/a/b/c/d". e.g. just executing sed s/c/e/
will just replace c with e in the line. Also, you don't need to change the complete line always. You can just change a character or a word in the text.Additionally, if a line contains more than one occurrence of character/word, this command will only change the first one e.g. if input string is x.y.z.zz=/a/b/c/d/c, executing sed s/c/e/ will have output x.y.z.zz=/a/b/e/d/c
If all the occurrences need to be changed g (global) needs to be added in sed command e.g. sed s/c/e/g will give output x.y.z.zz=/a/b/e/d/eIf sed needs to be executed only for a particular line, line number shall be mentioned in the sed command itself, as done in the question. This is the link (http://www.grymoire.com/Unix/Sed.html), I always refer when in question with sed

Bash - Changing configuration file with sed

I've been having some problems with a shell script that changes a configuration file named ".backup.conf".
The configuration file looks like this:
inputdirs=(/etc /etc/apm /usr/local)
outputdir="test_outputdir"
backupmethod="test_outputmethod"
loglocation="test_loglocation"`
My script needs to change one of the configuration file variables, and I've had no trouble with the last 3 variables.
If I wanted to change variable "inputdirs" /etc/ to /etc/perl, what expression should I use?
If I use echo with append, it will only append it to the end of the file.
I've tried using sed in the following format:
sed -i 's/${inputdirs[$((izbor-1))]}/$novi/g' .backup.conf where "izbor" is which variable I want to change from inputdirs and "novi" is the new path (e.g. /etc/perl).
So, with the following configuration file, and with variables $izbor=1and $novi=/etc/perl I should change the first variable inputdirs=/etc to /etc/perl
and the variable inputdirs should finally look like inputdirs=(/etc/perl /etc/apm /usr/local)
Thank you for your help!
You could try this:
enovi="$(printf '%s\n' "$novi" | sed -e 's/[\\&/]/\\&/g')"
izbor1="$(expr "$izbor" - 1)"
sed -rie "s/([(]([^ ]* ){$izbor1})[^ )]*/\\1$enovi/" config.txt
A summary of the commands:
The first line generates a variable $enovi that has the escaped contents of $novi. Basically,the following characters are escaped: &, \, and /. So /etc/perl becomes \/etc\/perl.
We create a new variable decrementing $izbor.
This is the actual substitute expression. I'll explain it in parts:
First we match the parenthesis character [(].
We will now search for a sequence of non-spaces followed by a space ([^ ]*).
This search (identified by grouping in the inner parenthesis) is repeated $izbor1 times ({$izbor1})
The previous expressions are grouped into an outer parenthesis group in order to be captured into an auxiliary variable \1.
We now match the word we want to replace. It is formed by a sequence of characters that aren't spaces and isn't a closing parenthesis (this is to handle the case of the last word)
The replacement is formed by the captured value \1, followed by our new string.
Hope this helps =)
If you are trying to use $izbor as an index, it will probably want to be a flag to s///. Assuming your input matches ^inputdirs=( (with no whitespace), you can probably get away with:
sed -i '/^inputdirs=(/{
s/(/( /; s/)/ )/; # Insert spaces inside parentheses
s# [^ ][^ ]* # '"$novi#$izbor"';
s/( /(/; s/ )/)/; } # Remove inserted spaces
' .backup.conf
The first two expressions ensure that you have whitespace inside the parentheses,
so may not be necessary if your input already has whitespace there. It's a bit obfuscated above, but basically the replacement you are doing is something like:
s# [^ ][^ ]* #/etc/perl#2
where the 2 flag tells sed to only replace the second occurrence of the match. This is really fragile, since it requires no whitespace before inputdirs and whitespace inside the parens and does not handle tabs, but it should work for you. Also, some sed allow [^ ][^ ]* to be written more simply as [^ ]+, but that is not universal.

Resources