For example, I got a line like this:
44567743346_567864_56788_5677_3
I want to change all the _
into :
, except for the last one would change into \
. The expected output is:
44567743346:567864:56788:5677\3
For example, I got a line like this:
44567743346_567864_56788_5677_3
I want to change all the _
into :
, except for the last one would change into \
. The expected output is:
44567743346:567864:56788:5677\3
$ echo '44567743346_567864_56788_5677_3' | sed -r 's|_|:|g; s|:([^:]*)$|\\\1|'
44567743346:567864:56788:5677\3
This uses two sed substitution commands. The first, s|_|:|g
changes all underlines to colons. The second, s|:([^:]*)$|\\\1|
finds the last colon and changes it to a backslash.
sed -r '1~4{s|_|:|g; s|:([^:]*)$|\\\1|;}' file
To modify the file in-place:
sed -i -r '1~4{s|_|:|g; s|:([^:]*)$|\\\1|;}' file
-r
option to enable extended regex and try again.
– John1024
Aug 15 '15 at 07:49
If you know the count of the last occurrence of _
e.g. in your example the last _
is the 4th _
in the line, you can do :
sed 's/_/\\/4; s/_/:/g' file.txt
Here we have first converted the 4th (last) occurrence of _
to \
, then the remaining occurrences of _
are converted to :
.
For example :
$ echo '44567743346_567864_56788_5677_3' | sed 's/_/\\/4; s/_/:/g'
44567743346:567864:56788:5677\3
On the other hand if you don't know the position of the last occurrence of _
, you can greedily match upto the last occurrence of _
, replace it with \
and then replace the rest with :
:
sed -r 's/^(.*)_/\1\\/; s/_/:/g' file.txt
For example :
$ echo '44567743346_567864_56788_5677_3' | sed -r 's/^(.*)_/\1\\/; s/_/:/g'
44567743346:567864:56788:5677\3
A Simple One Using sed
echo "44567743346_567864_56788_5677_3" | rev | sed "s/_/:/2g" | sed 's/_/\\/' | rev
Now let's see how it works:-
1) rev
reverses the string,
it becomes 3_7765_88765_468765_64334776544
2) sed "s/_/:/2g
replaces all occurrence of _
to :
except the first occurrence in reversed string,
it becomes, 3_7765:88765:468765:64334776544
3) Next sed
i.e. sed 's/_/\\/'
replaces just the first occurrence of _
to \
.
It becomes, 3\7765:88765:468765:64334776544
4) rev
reverses this processed string
Now, final string is:-
44567743346:567864:56788:5677\3
Now, its done!
Note: It doesn't give much efficiency. Its here just because its simple
Works in any line length:
$ echo "44567743346_567864_56788_5677_3" | sed -E 's/^(.*)_/\1\\/; s/_/:/g'
44567743346:567864:56788:5677\3
Why sed
if it's simpler using awk
?
awk -F_ '{printf "%s",$1; for(i=2;i<NF;i++){printf ":%s",$i}; printf "\\%s\n",$NF}' your_input_file
And as you said in a comment? you need only every fourth line
awk -F_ '(NR % 4) == 1 {printf "%s",$1; for(i=2;i<NF;i++){printf ":%s",$i}; printf "\\%s\n",$NF; next} {print}' your_input_file
Example
Input file
% cat foo
1_44567743346_567864_56788_5677_3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1_44567743346_567864_56788_5677_3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1_44567743346_567864_56788_5677_3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1_44567743346_567864_56788_5677_3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
Command output to change every fourth line
% awk -F_ '(NR % 4) == 1 {printf "%s",$1; for(i=2;i<NF;i++){printf ":%s",$i}; printf "\\%s\n",$NF; next} {print}' foo
1:44567743346:567864:56788:5677\3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1:44567743346:567864:56788:5677\3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1:44567743346:567864:56788:5677\3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
1:44567743346:567864:56788:5677\3
2_908798_08098_878_24332_24_24244_22
3_44567743346_567864_56788_5677_3
4_908798_08098_878_24332_24_24244_22
Command output to change each line
% awk -F_ '{printf "%s",$1; for(i=2;i<NF;i++){printf ":%s",$i}; printf "\\%s\n",$NF}' foo
1:44567743346:567864:56788:5677\3
2:908798:08098:878:24332:24:24244\22
3:44567743346:567864:56788:5677\3
4:908798:08098:878:24332:24:24244\22
1:44567743346:567864:56788:5677\3
2:908798:08098:878:24332:24:24244\22
3:44567743346:567864:56788:5677\3
4:908798:08098:878:24332:24:24244\22
1:44567743346:567864:56788:5677\3
2:908798:08098:878:24332:24:24244\22
3:44567743346:567864:56788:5677\3
4:908798:08098:878:24332:24:24244\22
1:44567743346:567864:56788:5677\3
2:908798:08098:878:24332:24:24244\22
3:44567743346:567864:56788:5677\3
4:908798:08098:878:24332:24:24244\22