replace string with special characters using sed - bash

I have the following string in a file: "dd/mmm/YYYY:HH:MM:SS -0500"
and would like it to be replaced with an actual date i.e. "17/Mar/2016:18:14:40 -0500"
I tried using sed, but the following doesn't seem to work
dateTemplate="dd/mmm/YYYY:HH:MM:SS -0500"
actualDate="17/Mar/2016:18:14:40 -0500"
sed -i "s#'$dateTemplate'#'$actualDate'#g" tmp.txt
Any help would be appreciated.

Try removing the single quotes(') from the sed program:
dateTemplate="dd/mmm/YYYY:HH:MM:SS -0500"
actualDate="17/Mar/2016:18:14:40 -0500"
sed -i "s#$dateTemplate#$actualDate#g" tmp.txt
Tested with GNU sed version 4.2.1
Before:
1. pre dd/mmm/YYYY:HH:MM:SS -0500 post
2. pre dd/mmm/YYYY:HH:MM:SS -0500 post
After:
1. pre 17/Mar/2016:18:14:40 -0500 post
2. pre 17/Mar/2016:18:14:40 -0500 post

EDIT: I noticed you were using a different delimiter. I can confirm that Michael Jaros' is a more accurate answer.
You will need to escape the slashes:
dateTemplate="dd/mmm/YYYY:HH:MM:SS -0500"
actualDate="17\/Mar\/2016:18:14:40 -0500"
sed -i "s/$dateTemplate/$actualDate/g" tmp.txt
I tested that working. Let me know if it doesn't!

I would automate the process a bit:
dateTemplate="dd/mmm/YYYY:HH:MM:SS -0500"
actualDateTemplate="+$(
sed '
s/\<dd\>/%d/g
s/\<mmm\>/%b/g
s/\<YYYY\>/%Y/g
s/\<HH\>/%H/g
s/\<MM\>/%M/g
s/\<SS\>/%S/g
# add more translations here if desired
' <<<"$dateTemplate"
)"
actualDate=$(date "$actualDateTemplate")
echo "foo $dateTemplate bar" | sed "s#$dateTemplate#$actualDate#g"
foo 17/Mar/2016:20:22:34 -0500 bar
There's always the danger that the dateTemplate variable will contain the character used as the s/// delimiter, no matter what character you use. This process can be done with string operations in the shell, but that's tedious.

Related

sed - remove dates from each line of file

I am trying to get rid of the dates - all of them from 2015 -present 2017.
I want to rename each foo_data_$date to just foo_data_*. I just need the files name. Not all the individual dates.
I do not understand the regex for sed - I can do it in perl with perl -nle 'print /(foo_data_)\d+txt) but can't figure out how to do it with sed.
I want to do it in sed because I have been using sed -i flag and changing the file in place.
cat /tmp/foo | head | sed -e 's/foo_data_20*txt/foo_data_\*/g'
foo_data_20150901.txt
foo_data_20150902.txt
foo_data_20150906.txt
foo_data_20150907.txt
foo_data_20150908.txt
foo_data_20150909.txt
foo_data_20150912.txt
You can just run sed like this.
sed -e 's/foo_data_[0-9]*/foo_data_/g'
Now, for the thing to capture dates only between 2015 and 2017, this will make it.
sed -e 's/foo_data_201\(5\|6\|7\)[0-9]*/foo_data_/g'
Then you will remove the dates from the file names in your file.
You don't need to mention foo_data:
sed -i 's/201[567][01][0-9][0-3][0-9]//'
Your command was wrong: /foo_data_20*txt/ will match a '0' 0 or more times (something like foo_data_2000000000000txt).
If you just want to rename the files, most Linux distros (assuming you're on Linux) have a rename utility that handles Perl regular expressions just fine:
pax> touch pax_100.txt ; touch pax_200.txt
pax> rename -n 's/_(\d)/_diablo_$1/' pax*
rename(pax_100.txt, pax_diablo_100.txt)
rename(pax_200.txt, pax_diablo_200.txt)
The -n options shows what will happen rather than doing the rename. Once you're satisfied, simply remove it.
Oh, and one final note. If you remove the dates from all those file names, they'll all have the same file name. Unless your file names are just test data, that's probably going to need some further thought on your part.

sed regex capturing group outputting whole input

I'm trying to use sed in order to get following output > 09 Aug 2017 14:15:11 from the string that looks like this 09/Aug/2017:14:15:11
when I use following code
sed 's/^\(\d+\)\/\(\w+\)\/\(\d+\)\:\(.*\)$/\1 /p' <(echo "09/Aug/2017:14:15:11")
I get whole input string as an output:
09/Aug/2017:14:15:11
Im doing this in order to execute date -d command on the result since date -d 09/Aug/2017:14:15:11 +%s is giving me this error: date: invalid date ‘09/Aug/2017:14:15:11’.
If you have other suggestion rather than using sed dont hesitate to make an suggestion.
Thanks!
With sed:
$ echo "09/Aug/2017:14:15:11" | sed -e 's#/# #g' -e 's/:/ /'
09 Aug 2017 14:15:11
We use two search and replace commands here, one running after the other. The first one to replace all (notice the global flag, g) slashes with spaces (/ → ), and the second one to replace just the first colon (: → ) (notice the lack of g flag). Both are search and replace commands (s), but the first one uses # as separator instead of the standard /, so we don't have to escape the slash we are searching.
I think command below is better:
date "+%d %h %Y %H:%M:%S"

sunOS's sed in bash script - replace pattern: nothing changes

ANSWER:
as mlv and sorontar mentioned, my SED version is BRE and doesn't support | (pipe). so in my case is possible use something like:
sed "s/\( [namevlu]*=\"\)BASE\.$str1/\1BASE\.$str2/g"
which match name=" and value=" but not other=" and values=". regex ( [namevlu]) contains only characters what i need. ok, it is not as save, as can be, but i don't expect existence of something like valuenm=" or so. if someone needs exactly specified regex, it needs make two (or more) seds.
ORIGINAL QUESTION:
I need to replace one string with another, but for sure i need check specific context.
for example:
blah val1="BASE.OLD_TEXT_OR_SO" blabla
blah val2="BASE.OLD_SOMETHING" ...
i want change to
blah val1="BASE.NEW_TEXT_OR_SO" blabla
blah val2="BASE.NEW_SOMETHING" ...
this script doesn't change anything:
#!/bin/bash
...
str1="OLD_"
str2="NEW_"
sed "s/\(name=\"|value=\"\)BASE\.$str1/\1BASE\.$str2/g" input.file > output.file
but later similiar sed works ok:
sed "s/\(<Tag>\)[A-Z0-9\-\._|]*\(<\/Tag>\)/\1$otherStr\2/g" input.file > output.file
output file has still BASE.OLD_ :/
also when i try it on console, i get same (none) result. i think there is something wrong in "looking for" pattern, but i havent idea what.
$ str0='blah name="BASE.OLD_TEXT_OR_SO" blabla
> blah value="BASE.OLD_SOMETHING" ...
> blah other="BASE.OLD_SOMETHING" ...
> blah values="BASE.OLD_SOMETHING" ...'
$ echo $str0 | sed "s/\(val1=\"|val2=\"\)BASE\.$str1/\1BASE\.$str2/g"
regex was tested on online tester where it works fine.
(name="|value=")BASE\.OLD_
\1BASE\.NEW_
system:
SunOS 5.11
GNU bash 4.1.11(1)-release
Sed 4.2.1
Thanks in advance.
I'm not sure SunOS sed includes |. That would be added with -r which I don't think SunOS sed supports.
But in this case, you should be able to do:
sed "s/\(val[12]=\"\)BASE.$str1/\1BASE.$str2/"
If you can't use val[12], then I don't think it can be done in sed. But it's not too hard in perl:
perl -pe "if (/val1=BASE.$str1/ || /val2=BASE.$str1/) {s/BASE.$str1/BASE.$str2/;}"
In a basic sed only BRE are supported, (POSIX BRE) and the alternation (|) does not exist.
Your regex must be written as:
sed "s/\(val[12]=\"BASE\.\)${str1}/\1${str2}/g" input.file
If, the version of sed you use supports (extended) ERE (which accepts the |) then you may use:
sed -E "s/(val(1|2)=\"BASE\.)${str1}/\1${str2}/g" input.file
as mlv and sorontar mentioned, my SED version is BRE and doesn't support | (pipe). so in my case is possible use something like:
sed "s/\( [namevlu]*=\"\)BASE\.$str1/\1BASE\.$str2/g"
which match name=" and value=" but not other=" and values=". regex ( [namevlu]) contains only characters what i need. ok, it is not as save, as can be, but i don't expect existence of something like valuenm=" or so. if someone needs exactly specified regex, it needs make two (or more) seds.

Sed is not replacing all occurrences of pattern

I've got a the following variable LINES with the format date;album;song;duration;singer;author;genre.
August 2013;MDNA;Falling Free;00:31:40;Madonna;Madonna;Pop
August 2013;MDNA;I don't give a;00:45:40;Madonna;Madonna;Pop
August 2013;MDNA;I'm a sinner;01:00:29;Madonna;Madonna;Pop
August 2013;MDNA;Give Me All Your Luvin';01:15:02;Madonna;Madonna;Pop
I want to output author-song, so I made this script:
echo $LINES | sed s_"^[^;]*;[^;]*;\([^;]*\);[^;]*;[^;]*;\([^;]*\)"_"\2-\1"_g
The desired output is:
Madonna-Falling Free
Madonna-I don't give a
Madonna-I'm a sinner
Madonna-Give Me All Your Luvin'
However, I am getting this:
Madonna-Falling Free;Madonna;Pop August 2013;MDNA;I don't give a;00:45:40;Madonna;Madonna;Pop August 2013;MDNA;I'm a sinner;01:00:29;Madonna;Madonna;Pop August 2013;MDNA;Give Me All Your Luvin';01:15:02;Madonna;Madonna;Pop
Why?
EDIT: I need to use sed.
When I run your sed script on your input, I get this output:
Madonna-Falling Free;Pop
Madonna-I don't give a;Pop
Madonna-I'm a sinner;Pop
Madonna-Give Me All Your Luvin';Pop
which is fine except for the extra ;Pop - you just need to add .*$ to the end of your regex so that the entire line is replaced.
Based on your reported output, I'm guessing your input file is using a different newline convention from what sed expects.
In any case, this is a pretty silly thing to use sed for. Much better with awk, for instance:
awk 'BEGIN {FS=";";OFS="-"} {print $5,$3}'
or, slightly more tersely,
awk -F\; -vOFS=- '{print $5,$3}'
If you want sed to see more than one line of input, you must quote the variable to echo:
echo "$LINES" | sed ...
Note that I'm not even going to try to evaluate the correctness of your sed script; using sed here is a travesty, given that awk is so much better suited to the task.
It looks like sed is viewing your entire sample text as a single line. So it is performing the operation requested and then leaving the rest unchanged.
I would look into the newline issue first. How are you populating $LINES?
You should also add to the pattern that seventh field in your input (genre), so that the expression actually does consume all of the text that you want it to. And perhaps anchor the end of the pattern on $ or \b (word boundary) or \s (a spacey character) or \n (newline).
If your format is absolutely permanent, just try below:
echo $line | sed 's#.*;.*;\(.*\);.*;.*;\(.*\);.*#\2-\1#'

sed: mass converting epochs amongst random other text

Centos / Linux
Bash
I have a log file, which has lots of text in and epoch numbers all over the place. I want to replace all epochs whereever they are into readable date/time.
I've been wanting to this via sed, as that seems the tool for the job. I can't seem to get the replacement part of sed to actually parse the variable(epoch) to it for conversion.
Sample of what I'm working with...
echo "Some stuff 1346474454 And not working" \
| sed 's/1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/'"`bpdbm -ctime \&`"'/g'
Some stuff 0 = Thu Jan 1 01:00:00 1970 And not working
The bpdbm part will convert a supplied epoch variable into useful date. Like this..
bpdbm -ctime 1346474454
1346474454 = Sat Sep 1 05:40:54 2012
So how do i get the "found" item to be parsed into a command. As i don't seem to be able to get it to work.
Any help would be lovely. If there is another way, that would be cool...but i suspect sed will be quickest.
Thanks for your time!
that seems the tool for the job
No, it is not. sed can use & only itself, there is no way how to make it an argument to a command. You need something more powerful, e.g. Perl:
perl -pe 'if ( ($t) = /(1[0-9]+)/ ) { s/$t/localtime($t)/e }'
You can do it with GNU sed, the input:
infile
Some stuff 1346474454 And not working
GNU sed supports /e parameter which allows for piping command output into pattern space, one way to take advantage of this with bpdbm:
sed 's/(.*)(1[0-9]{9})(.*)/echo \1 $(bpdbm -ctime \2) \3/e' infile
Or with coreutils date:
sed 's/(.*)(1[0-9]{9})(.*)/echo \1 $(date -d #\2) \3/e' infile
output with date
Some stuff Sat Sep 1 06:40:54 CEST 2012 And not working
To get the same output as with bpdbm:
sed 's/(.*)(1[0-9]{9})(.*)/echo "\1$(date -d #\2 +\"%a %b %_d %T %Y\")\3"/e' infile
output
Some stuff Sat Sep 1 06:40:54 2012 And not working
Note, this only replaces the last epoch found on a line. Re-run if there are more.

Resources