sed append line - how do I get a new line? - bash

I have spent hours on this but can't crack it. I am using sed on OSX .
This is the code:
sed -i.bak "s/^\(\$dokuwiki_hash.*\)$/\1\n '"$date"' => '"$hash"',/" install.php
And the output that I get which is wrong is (see the first line):
$dokuwiki_hash = array(n '2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623',
'2005-09-22' => 'e33223e957b0b0a130d0520db08f8fb7',
'2006-03-05' => '51295727f79ab9af309a2fd9e0b61acc',
'2006-03-09' => '51295727f79ab9af309a2fd9e0b61acc',
'2006-11-06' => 'b3a8af76845977c2000d85d6990dd72b',
'2007-05-24' => 'd80f2740c84c4a6a791fd3c7a353536f',
'2007-06-26' => 'b3ca19c7a654823144119980be73cd77',
'2008-05-04' => '1e5c42eac3219d9e21927c39e3240aad',
'2009-02-14' => 'ec8c04210732a14fdfce0f7f6eead865',
'2009-12-25' => '993c4b2b385643efe5abf8e7010e11f4',
'2010-11-07' => '7921d48195f4db21b8ead6d9bea801b8',
'2011-05-25' => '4241865472edb6fa14a1227721008072',
'2011-11-10' => 'b46ff19a7587966ac4df61cbab1b8b31',
'2012-01-25' => '72c083c73608fc43c586901fd5dabb74',
'2012-09-10' => 'eb0b3fc90056fbc12bac6f49f7764df3',
'2013-04-06' => '7b62b75245f57f122d3e0f8ed7989623',
);
It should be on a new line like below:
$dokuwiki_hash = array(
'2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623',
'2005-09-22' => 'e33223e957b0b0a130d0520db08f8fb7',
'2006-03-05' => '51295727f79ab9af309a2fd9e0b61acc',
'2006-03-09' => '51295727f79ab9af309a2fd9e0b61acc',
'2006-11-06' => 'b3a8af76845977c2000d85d6990dd72b',
'2007-05-24' => 'd80f2740c84c4a6a791fd3c7a353536f',
'2007-06-26' => 'b3ca19c7a654823144119980be73cd77',
'2008-05-04' => '1e5c42eac3219d9e21927c39e3240aad',
'2009-02-14' => 'ec8c04210732a14fdfce0f7f6eead865',
'2009-12-25' => '993c4b2b385643efe5abf8e7010e11f4',
'2010-11-07' => '7921d48195f4db21b8ead6d9bea801b8',
'2011-05-25' => '4241865472edb6fa14a1227721008072',
'2011-11-10' => 'b46ff19a7587966ac4df61cbab1b8b31',
'2012-01-25' => '72c083c73608fc43c586901fd5dabb74',
'2012-09-10' => 'eb0b3fc90056fbc12bac6f49f7764df3',
'2013-04-06' => '7b62b75245f57f122d3e0f8ed7989623',
);
Any help would be greatly appreciated !

Don't use substitute for this, sed has a perfectly good append command for adding a line after the current one, without fiddling around with newline or storage of regex results:
pax> echo '$dokuwiki_hash = array(
'"'"'2013-03-17'"'"' => '"'"'7b62b75245f57f122d3e0f8ed7989623'"'"'
);' | sed '/^\$dokuwiki_hash = /a\ blah '"'"'blah'"'"' blah'
$dokuwiki_hash = array(
blah 'blah' blah
'2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623'
);
The machinations with the quotes are to allow you to put literal single quotes within the command.
Alternatively, you can use double quotes on the outside, you just have to be careful that the shell doesn't interpret your dollar-variables:
pax> echo "\$dokuwiki_hash = array(
'2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623'
);" | sed "/^\$dokuwiki_hash = /a\ blah 'blah' blah"
$dokuwiki_hash = array(
blah 'blah' blah
'2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623'
);
There's also an opposing insert command i for inserting before the current line but it's append you want in this case.
And, if you're having trouble with mixing quote types (perhaps due to an older bash under OSX), you can put the sed commands into a file and use sed -f to run them:
pax> cat qq.sed
/^$dokuwiki_hash = /a\ blah 'blah' blah
pax> echo '$dokuwiki_hash = array(
'"'"'2013-03-17'"'"' => '"'"'7b62b75245f57f122d3e0f8ed7989623'"'"'
);' | sed -f qq.sed
$dokuwiki_hash = array(
blah 'blah' blah
'2013-03-17' => '7b62b75245f57f122d3e0f8ed7989623'
);
That gets around any quoting battles between the shell and sed. If that still doesn't work, see this link, which suggests installing GNU sed instead.

\n is not supported as a newline character in the replacement part of the substitute command of regular sed (only in GNU sed).
For example to prepend a newline to a pattern, instead of
sed 's/pattern/\n&/' file
use
sed 's/pattern/\
&' file
The \ should be the last character on the line.

Related

Replacing a certain number of characters after a match using sed with {,/ and "

I need to replace whatever is there after \"db_password\":\" up to 16 characters (passwords) with <sensitive>:
data_json: "{\"db_password\":\"qwHLI?mkSrQ=GHU_\"}" => "{\"db_password\":\"BoBBsR9PA]wZ_3AC\"}"
should be
data_json: "{\"db_password\":\"sensitive\"}" => "{\"db_password\":\"sensitive\"}"
I have tried following but not sure how to escape {, \ and ".
sed -E 's/("{\"db_password\":\").{16}/\<sensitive>/'
cat jsonfile
#=> data_json: "{\"db_password\":\"qwHLI?mkSrQ=GHU_\"}" => "{\"db_password\":\"BoBBsR9PA]wZ_3AC\"}"
sed -E 's/("\{\\"db_password\\":\\").{16}/\1sensitive/g' jsonfile
#=> data_json: "{\"db_password\":\"sensitive\"}" => "{\"db_password\":\"sensitive\"}"
You need escape \ too. Also \1 to reference the first pair () matched content.
<> is unnessary here.
Update: without the 16 chars password limitation:
sed -E 's/("\{\\"db_password\\":\\")[^"\]+/\1sensitive/g' jsonfile
#=> data_json: "{\"db_password\":\"sensitive\"}" => "{\"db_password\":\"sensitive\"}"
This will replace password of any passwords. (Well, can't have \ in passwords.)

What is => mean in my bash script

I have this line in a bash script and I can't figure out what the "=>" means? I don't think it means equal to or greater than but maybe it does. Thoughts?
"echo '\"postgres\" => { \"archive_timeout\" => 300, \"backup\" => 1, \"base_backup_interval\" => 3600, \"restore\" => 1 },' >> /tmp/user_data.config\n",
It doesn't mean anything, because it's inside a string. Consider:
$ echo 'foo'
foo
$ echo 'foo => bar'
foo => bar
The => doesn't have any significance; it's just part of the string that echo writes to its output.
In the case of your code, the echo command and its string argument are followed by >> /tmp/user_data.config, which just means that the output will be appended to the user_data.config file. Like so:
$ touch /tmp/out.txt
$ echo 'foo => bar' >> /tmp/out.txt
$ echo 'baz => qux' >> /tmp/out.txt
$ cat /tmp/out.txt
foo => bar
baz => qux
The => is part of the string that is being echo'd. Try running the command (without the " at the beginning and the \n", at the end) in BASH and you'll see it just echoes the string and appends it to /tmp/user_data.config
$ echo '\"postgres\" => { \"archive_timeout\" => 300, \"backup\" => 1, \"base_backup_interval\" => 3600, \"restore\" => 1 },' >> /tmp/user_data.config
$ cat /tmp/user_data.config
\"postgres\" => { \"archive_timeout\" => 300, \"backup\" => 1, \"base_backup_interval\" => 3600, \"restore\" => 1 },

'grep' a file name, but only show

I have files that are named ACC160-MASTER-201503, ACC160L-MASTER-201503 and IS160-MASTER-201503. I'm basically trying to output the first few letters (in the example, just ACC and IS) and not the numbers. After that I need another grep to only grab up to the - (in the example, ACC160, ACC160L, and IS160).
If this is bash/ksh/zsh/sh/..., just use parameter substitution:
for file in ACC160-MASTER-201503 ACC160L-MASTER-201503 IS160-MASTER-201503; do
alnum=${file%%-*} # remove the first "-" and following chars
alpha=${alnum%%[0-9]*} # remove the first digit and all following chars
echo "$file => $alnum => $alpha"
done
ACC160-MASTER-201503 => ACC160 => ACC
ACC160L-MASTER-201503 => ACC160L => ACC
IS160-MASTER-201503 => IS160 => IS
If you want to get all the filename until the first '-' then grep -oP ".*?(?=-)" should do the trick.
-o is the only-matching option
-P enables Perl syntax
.*? is a reluctant quantifiers, it reads while looking for the next motif in the regex
(?=-) is a lookahead tag: it tells you what to expect (-) after the matching substring

Chaining sed statements

I'm running a dozen of sed commands for each Capistranio deploy and I was wondering, if it's possible to chain them into 1 single sed command, instead of firing dozens at the server.
task :taskname do
{:'foo' => foo, :'bar' => bar, :'foobar' => foobar, :'fubar' => fubar }.each do |search, replace|
run "sed -i 's/#{search}/#{replace}/' file.ext"
end
end
sed natively accepts a dozen of patterns (if you for some reason prefer sed):
{:foo => foo, :bar => bar, :foobar => foobar, :fubar => fubar}.inject("") do |acc, k, v|
acc += " -e 's/#{k}/#{v}'"
end
run "sed #{acc} file.ext"
Does mudasobwa's code work? With my Ruby (v1.9.3), it has to be:
acc = {:foo => foo, :bar => bar, :foobar => foobar, :fubar => fubar}.inject("") do |m, p|
m + " -e 's/#{p[0]}/#{p[1]}'"
end
run "sed #{acc} file.ext"

Bash Sed replace

I'm working in a Zend CRUD generator and I have to replace the word "test" which is in $targetForm file with the form code of each field.
field[0]="foo"
field[1]="bar"
textareafield='$'"acme_en = new Utils_Form_Element_Textarea('acme_en',array('langblock'=>'en', 'isWysiwyg' => true));
"'$'"this->addElement("'$'"acme_en);
"'$'"this->addElement('textarea','acme_fr', array( 'label'=>__('acme'), 'langblock'=>'fr', 'isWysiwyg' => true, 'altLangElem' => "'$'"acme_en));"
for ((i=0; i<${#field[#]}; i++));
do
formfield[$i]=$textareafield
formfield[$i]=${formfield[$i]//acme/${field[$i]}}
echo ${formfield[$i]}
sed -i "s/test/test\n ${formfield[$i]}/" $targetForm
done
The command line says:
$foo_en = new Utils_Form_Element_Textarea('foo_en', array('langblock'=>'en', 'isWysiwyg' => true)); $this->addElement($foo_en); $this->addElement('textarea','foo_fr', array( 'label'=>__('foo'), 'langblock'=>'fr', 'isWysiwyg' => true, 'altLangElem' => $foo_en));
sed: -e expression #1, char 120: unterminated `s' command
$bar_en = new Utils_Form_Element_Textarea('bar_en', array('langblock'=>'en', 'isWysiwyg' => true)); $this->addElement($bar_en); $this->addElement('textarea','bar_fr', array( 'label'=>__('bar'), 'langblock'=>'fr', 'isWysiwyg' => true, 'altLangElem' => $bar_en));
sed: -e expression #1, char 120: unterminated `s' command
Maybe there's a problem with the specials character but I don't know how to solve it.
This error you get very likely has nothing to do with using \n in sed substitution (especially since you mentioned in the comments that you are using GNU sed version 4.2.1).
The real culprit is textareafield which contains a multi-line string.
What happens is that when ${formfield[$i]} gets expanded, your sed command looks like this
sed -i "s/test/test\n line1
line2
line3/"
This is problematic because without terminating lines with a literal \, sed would interpret each line as a complete command in itself, in this case
s/test/test\n line1
which is missing a / at the end, hence the error "unterminated `s' command".
To fix this, what we want is to insert a \ to the end of every line except the last, i.e.
sed -i "s/test/test\n line1\
line2\
line3/"
I got your example to work by adding two lines
for ((i=0; i<${#field[#]}; i++));
do
formfield[$i]=$textareafield
formfield[$i]=${formfield[$i]//acme/${field[$i]}}
formfield[$i]=${formfield[$i]//\;/\;\\}
formfield[$i]=${formfield[$i]%\\}
echo ${formfield[$i]}
sed -i "s/test/test\n ${formfield[$i]}/" $targetForm
done
Since all the lines in textareafield end in ;, I replaced all ; with ;\, then removed the last \ from the last line.
sed does not support \n in the substitution, but you can use a literal newline if you escape it. Also, you need to quote the ' characters. Here is one way of doing it:
for ((i=0; i<${#field[#]}; i++));
do
formfield[$i]=$textareafield
formfield[$i]=${formfield[$i]//acme/${field[$i]}}
sedexpr="${formfield[$i]}"
sedexpr="${sedexpr//\'/\'}"
sedexpr="${sedexpr//
/\\
}"
sed -i "s/test/test\
${sedexpr}/" $targetForm
done
But at this level of complexity you're probably better off switching to something like Python.

Resources