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