sed output in case of not match - bash

I want to extract some fields from a line, like:
echo "aaa,bbb-ccc,ddd" | sed 's/.*,\(.*\)-\(.*\),.*/\1 \2/'
It will output "bbb ccc" as i expect. But if the input line is not of the pattern,
echo "aaa,bbb,ccc,ddd" | sed 's/.*,\(.*\)-\(.*\),.*/\1 \2/'
It will output the whole line "aaa,bbb,ccc,ddd". What i want is when not matched, output nothing. How can i do it?

sed -n 's/.*,\(.*\)-\(.*\),.*/\1 \2/p'
Example:
$ echo "aaa,bbb-ccc,ddd" | sed -n 's/.*,\(.*\)-\(.*\),.*/\1 \2/p'
bbb ccc
$ echo "aaa,bbb,ccc,ddd" | sed -n 's/.*,\(.*\)-\(.*\),.*/\1 \2/p'
$
-n disables printing of the pattern space by default.
p makes sed print when matched.

echo "a-b" | sed -n 's/-/#/p'
a#b
echo "a_b" | sed -n 's/-/#/p'
-n means 'no output' as default.
/p means print

If that's the case, think of a simpler approach
echo "aaa,bbb-ccc,ddd" | awk -F"," '$2 ~ /-/{ print $2}'

Related

How to remove first & last character in bash string

#!/bin/bash
MA=$(bt-device -l | cut -d " " -f 3)
MAC=${MA:1: -1}
bluetoothctl connect $MAC
Expected Result
98:9E:63:18:00:88
Actual result
(98:9E:63:18:00:88
A few alternatives:
$ echo 'Denny’s Tunez (98:9E:63:18:00:88)' | sed -En 's/^[^(]*\(([^)]*)\).*/\1/p'
98:9E:63:18:00:88
$ echo 'Denny’s Tunez (98:9E:63:18:00:88)' | cut -d'(' -f2 | cut -d')' -f1
98:9E:63:18:00:88
$ echo 'Denny’s Tunez (98:9E:63:18:00:88)' | awk -F'[)(]' '{print $2}'
98:9E:63:18:00:88
$ echo 'Denny’s Tunez (98:9E:63:18:00:88)' | grep -Eow '(..)(:..){5}'
98:9E:63:18:00:88
$ x='Denny’s Tunez (98:9E:63:18:00:88)'
$ y="${x//*\(/}"
$ y="${y//\)*}"
$ echo $y
98:9E:63:18:00:88
With GNU bash and its Parameter Expansion:
s="(98:9E:63:18:00:88)"
s="${s/#?/}" # remove first character
s="${s/%?/}" # remove last character
echo "$s"
Output:
98:9E:63:18:00:88
Using sed it can be done in a single step:
s='Denny’s Tunez (98:9E:63:18:00:88)'
echo "$s" | sed -E 's/.* \(|)//g'
98:9E:63:18:00:88
So for your example you can use:
mac=$(bt-device -l | sed -E 's/.* \(|)//g')
You can use parameter expansion:
offset and length
echo ${MA:1: -1}
prefix and suffix removal
tmp=${MA#(}
echo ${tmp%)}
parameter matching
tmp=${MA/#\(}
echo ${tmp/%\)}
Another approach is to:
whitelist what you do want
echo "$MA" | tr -dC '[0-9A-F:]'

How to trim a string in shell script

I have a string,
var=refs/heads/testing/branch
I want to get rid of refs/heads/ in the string using shell script, such that I have only:
var=testing/branch
Commands I tried (one per line):
echo $(var) | awk -F\\ {'print $2'}
echo $var | sed -e s,refs/heads/,,
echo "refs/heads/testing/branch" | grep -oP '(?<=refs/heads/\)\w+'
echo "refs/heads/testing/branch" | LC_ALL=C sed -e 's/.*\\//'
echo "refs/heads/testing/branch" | cut -d'\' -f2
echo refs/heads/testing/branch | sed -e s,refs/heads/,,
there are lots of options out there ,try easy ones:
echo $var | cut -d "/" -f 3,4
echo $var | awk -F"/" '{print $3"/"$4}'
Shell parameter expansion: remove the prefix "refs/heads/" from the variable contents
$ var=refs/heads/testing/branch
$ echo "${var#refs/heads/}"
testing/branch

Extract all ip addresses with sed and awk from a string

It is simple to extract all ip addresses with grep from a string.
string="221.11.165.237xxxx221.11.165.233\n
219.158.9.97ttttt219.158.19.137"
echo $string |grep -oP "(\d+\.){3}\d+"
221.11.165.237
221.11.165.233
219.158.9.97
219.158.19.137
The regrex pattern is simple (\d+\.){3}\d+.
Do the same job with sed and awk.
For sed:
echo $string | sed 's/^\(\(\d\+\.\)\{3\}\d\+\)$/\1/g'
221.11.165.237xxxx221.11.165.233\n 219.158.9.97ttttt219.158.19.137
For awk:
echo $string |gawk 'match($0,/(\d+\.){3}\d+/,k){print k}'
echo $string |awk '/(\d+\.){3}\d+/{print}'
How to fix it for sed and gawk(awk)?
The expect output is the same as grep.
221.11.165.237
221.11.165.233
219.158.9.97
219.158.19.137
Very few tools will recognize \d as meaning digits. Just use [0-9] or [[:digit:]] instead:
$ echo "$string" | awk -v RS='([0-9]+\\.){3}[0-9]+' 'RT{print RT}'
221.11.165.237
221.11.165.233
219.158.9.97
219.158.19.137
The above uses GNU awk for multi-char RS and RT. With any awk:
$ echo "$string" | awk '{while ( match($0,/([0-9]+\.){3}[0-9]+/) ) { print substr($0,RSTART,RLENGTH); $0=substr($0,RSTART+RLENGTH) } }'
221.11.165.237
221.11.165.233
219.158.9.97
219.158.19.137
This might work for you (GNU sed):
sed '/\n/!s/[0-9.]\+/\n&\n/;/^\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}\n/P;D' file
Insert newlines either side of strings consisting only of numbers and periods. If a line contains only an IP address print it.
An easier-on-the-eye rendition uses the -r option:
sed -r '/\n/!s/[0-9.]+/\n&\n/;/^([0-9]{1,3}\.){3}[0-9]{1,3}\n/P;D' <<<"$string"
As you weren't specific about what could be between the ip addresses, I went with the fact that only numbers and periods will be in the ip:
echo "$string" | sed -r 's/[^0-9.]+/\n/'
echo "$string" | awk '1' RS="[^0-9.]+"

output of sed gives strange result when using capture groups

I'm doing the following command in a bash:
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' | sed -rn 's#^URL: \^/tags/([^/]+)/#\1#p'
I think this should output only the matching lines and the content of the capture group. So I'm expecting 0.0.0 as the result. But I'm getting 0.0.0abcd
Why contains the capture group parts from the left and the right side of the /? What I am doing wrong?
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' |
sed -rn 's#^URL: \^/tags/([^/]+)/#\1#p'
echo outputs two lines:
UNUSED
URL: ^/tags/0.0.0/abcd
The regular expression given to sed does not match the first line, so this line is not printed. The regular expression matches the second line, so URL: ^/tags/0.0.0/ is replaced with 0.0.0; only the matched part of the line is replaced, so abcd is passed unchanged.
To obtain the desired output you must also match abcd, for example with
sed -rn 's#^URL: \^/tags/([^/]+)/.*#\1#p'
where the .* eats all characters to the end of the line.
You can use awk:
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' | awk -F/ 'index($0, "^/tags/"){print $3}'
0.0.0
This awk command uses / as field delimiter and prints 3rd column when there ^/tags/ text in input.
Alternatively, you can use gnu grep:
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' | grep -oP '^URL: \^/tags/\K([^/]+)'
0.0.0
Or this sed:
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' | sed -nE 's~^URL: \^/tags/([^/]+).*~\1~p'
0.0.0
This sed catch your desired output.
echo -e 'UNUSED\nURL: ^/tags/0.0.0/abcd' | sed -E '/URL/!d;s#.*/(.*)/[^/]*#\1#'

shortening headers using awk

I have headers like
>XX|6226515|new|xx_000000.1| XXXXXXX
in a text file which I am trying shorten to
>XX6226515
using awk. I tried
awk -F"|" '/>/{$0=">"$1}1' input.txt > output.txt
but it yields the following instead
>XX|6226515|new|
awk -F"|" '{print $1$2}' input.txt > output.txt
Output:
>XX6226515
sed solution:
sed -e 's/|//' -e 's/|.*//'
The first substitution removes the first vertical bar, the second one removes the second one and anything after it.
$ awk -F'|' '$0=$1$2' <<< ">XX|6226515|new|xx_000000.1| XXXXXXX"
>XX6226515
This cut can also make it:
cut -d"|" --output-delimiter="" -f-2
See output:
$ echo ">XX|6226515|new|xx_000000.1| XXXXXXX" | cut -d"|" --output-delimiter="" -f-2
>XX6226515
-d"|" sets | as field delimiter.
--output-delimiter="" indicates that the output delimiter has to be empty.
-f-2 indicates that it has to print all records up to the 2nd (inclusive).
Also with just bash:
while IFS="|" read a b _
do
echo "$a$b"
done <<< ">XX|6226515|new|xx_000000.1| XXXXXXX"
See output:
$ while IFS="|" read a b _; do echo "$a$b"; done <<< ">XX|6226515|new|xx_000000.1| XXXXXXX"
>XX6226515

Resources