SED invalid command code for JSON response - regex

I am trying to get a value from a JSON from my local server (https://regex101.com/r/qeGcGu/1) on a headless mac mini (catalina), via sed. However, with the sed command I'd expect to work:
usr#mcMini ~/Documents/qBitTorrent cat /tmp/json.out | sed -i.bak '"hash":"(.*?)"'
sed: 1: ""hash":"(.*?)"": invalid command code "
usr#mcMini ~/Documents/qBitTorrent cat /tmp/json.out | sed -i.bak '\"hash\":\"(.*?)\"'
sed: 1: "\"hash\":\"(.*?)\"": unterminated regular expression
usr#mcMini ~/Documents/qBitTorrent cat /tmp/json.out | sed -i '' '\"hash\":\"(.*?)\"'
sed: 1: "\"hash\":\"(.*?)\"": unterminated regular expression
usr#mcMini ~/Documents/qBitTorrent cat /tmp/json.out | sed -i '' '"hash":"(.*?)"'
sed: 1: ""hash":"(.*?)"": invalid command code "
The file that I am trying to get the string from is a raw json.
[{"added_on":1587102956,"amount_left":0,"auto_tmm":false,"availability":-1,"category":"radarr","completed":1218638934,"completion_on":1587108704,"dl_limit":-1,"dlspeed":0,"downloaded":1220894674,"downloaded_session":0,"eta":8640000,"f_l_piece_prio":false,"force_start":true,"hash":"87802183fc647548ec6efe18feb16149522f6aa0","last_activity":1587119220,"magnet_uri":"magnet:?xt=urn:btih:87802183fc647548ec6efe18feb16149522f6aa0&dn=Fantasia%202000%20(1999)%20%5b1080p%5d%20%5bYTS.AG%5d&tr=udp%3a%2f%2ftracker.coppersurfer.tk%3a6969%2fannounce&tr=udp%3a%2f%2f9.rarbg.com%3a2710%2fannounce&tr=udp%3a%2f%2fp4p.arenabg.com%3a1337&tr=udp%3a%2f%2ftracker.leechers-paradise.org%3a6969&tr=udp%3a%2f%2ftracker.internetwarriors.net%3a1337&tr=udp%3a%2f%2ftracker.opentrackr.org%3a1337%2fannounce&tr=udp%3a%2f%2ftracker.zer0day.to%3a1337%2fannounce&tr=udp%3a%2f%2ftracker.leechers-paradise.org%3a6969%2fannounce&tr=udp%3a%2f%2fcoppersurfer.tk%3a6969%2fannounce","max_ratio":-1,"max_seeding_time":-1,"name":"Fantasia 2000 (1999) [1080p] [YTS.AG]","num_complete":22,"num_incomplete":4,"num_leechs":0,"num_seeds":0,"priority":0,"progress":1,"ratio":0.1782183661159947,"ratio_limit":-2,"save_path":"/Volumes/1049/Media/","seeding_time_limit":-2,"seen_complete":1587118087,"seq_dl":false,"size":1218638934,"state":"forcedUP","super_seeding":false,"tags":"","time_active":13224,"total_size":1218638934,"tracker":"udp://tracker.coppersurfer.tk:6969/announce","up_limit":-1,"uploaded":217585854,"uploaded_session":128831791,"upspeed":0}]
Actually what I want to accomplish is to get the first 6 chars from hash:
"hash":"87802183fc647548ec6efe18feb16149522f6aa0"
In this case my desired value is 878021
Could you please guide me in the correct direction?

You may use
sed -n 's/.*"hash":"\([^"]*\).*/\1/p' /tmp/json.out
Here, note that the file can be provided directly to the sed command, no need piping it with cat.
How it works
-n - option that suppresses the default line output (by default, sed will output non-matching lines)
s/ - substitute command (we are replacing)
.*"hash":"\([^"]*\).* - matches
.* - 0+ chars
"hash":" - "hash":" substring
\([^"]*\) - Group 1 (capturing group, \1 is used in the replacement part to refer to this value) - any 0+ chars other than "
.* - 0+ chars
\1 - the replacement is Group 1 value (it is all that remains on the matching line)
p - if there was a valid replacement print the result after replacement only.

Related

Sed: can not replace part of the string whithout replacing all of it

I am trying to replace part of the string, but can not find a proper regex for sed to execute it properly.
I have a string
/abc/foo/../bar
And I would like to achive the following result:
/abc/bar
I have tried to do it using this command:
echo $string | sed 's/\/[^:-]*\..\//\//'
But as result I am getting just /bar.
I understand that I must use group, but I just do not get it.
Could you, please, help me to find out this group that could be used?
You can use
#!/bin/bash
string='/abc/foo/../bar'
sed -nE 's~^(/[^/]*)(/.*)?/\.\.(/[^/]*).*~\1\3~p' <<< "$string"
See the online demo. Details:
-n - suppresses default line output
E - enables POSIX ERE regex syntax
^ - start of string
(/[^/]*) - Group 1: a / and then zero or more chars other than /
(/.*)? - an optional group 2: a / and then any text
/\.\. - a /.. fixed string
(/[^/]*) - Group 3: a / and then zero or more chars other than /
.* - the rest of the string.
\1\3 replaces the match with Group 1 and 3 values concatenated
p only prints the result of successful substitution.
You can use a capture group for the first part and then match until the last / to remove.
As you are using / to match in the pattern, you can opt for a different delimiter.
#!/bin/bash
string="/abc/foo/../bar"
sed 's~\(/[^/]*/\)[^:-]*/~\1~' <<< "$string"
The pattern in parts:
\( Capture group 1
/[^/]*/ Match from the first till the second / with any char other than / in between
\) Close group 1
[^:-]*/ Match optional chars other than : and - then match /
Output
/abc/bar
Using sed
$ sed 's#^\(/[^/]*\)/.*\(/\)#\1\2#' input_file
/abc/bar
or
$ sed 's#[^/]*/[^/]*/##2' input_file
/abc/bar
Using awk
string='/abc/foo/../bar'
awk -F/ '{print "/"$2"/"$NF}' <<< "$string"
#or
awk -F/ 'BEGIN{OFS=FS}{print $1,$2,$NF}' <<< "$string"
/abc/bar
Using bash
string='/abc/foo/../bar'
echo "${string%%/${string#*/*/}}/${string##*/}"
/abc/bar
Using any sed:
$ echo "$string" | sed 's:\(/[^/]*/\).*/:\1:'
/abc/bar

Match from beginning to word as long as there are no . in between: Convert grep -Po command to sed

I have made the following command to be able to match the string from the beginning of the line until the first occurrence of ".enabled" as long as there are no "." in between.
grep -Po '^\K[\w-]*?(?=\.enabled)'
input:
a-b-c.a.enabled.xxx.xx
a-b-c.a.b.enabled.xxx.xx
a-b-c.enabled.xxx.xx
output:
a-b-c
It runs properly on my local env with grep v3.1 but on Busybox v1.28.4 it says "grep: unrecognized option: P"
For that reason, I would like to convert this command to sed. Any input would be really helpful.
You can use
awk -F'.' '$2 == "enabled"{print $1}' file
sed -n 's/^\([^.]*\)\.enabled.*/\1/p' file
See the online demo.
Details:
awk:
-F'.' - the field separator is set to a .
$2 == "enabled" - if Group 2 value is enabled, then
{print $1} - print Field 1 value
sed:
-n - suppresses default line output in the sed command
s/^\([^.]*\)\.enabled.*/\1/p - finds any zero or more chars other than . at the start of string (placing them into Group 1, \1), then a .enabled and then the rest of the string and replaces with the Group 1 value, and prints the resulting value.
You may use this equivalent sed of your grep -P command:
sed -nE 's/^([-_[:alnum:]]+)\.enabled.*/\1/p' file
a-b-c
Details:
-n: Suppress notmal output
-E: Enables extended regex mode
([-_[:alnum:]]+): -_[:alnum:]]is equivalent of [-\w] or [-_a-zA-Z0-9]. It matches 1+ of these characters and captures them in group #1
\.enabled.*: matches .enabled followed by 0 or more of any string
\1: is replacement string that put value captured in capture group #1 back in replacement
With your shown samples, you could try following.
awk -F'\\.enabled' '$1~/^[-_[:alnum:]]+$/{print $1}' Input_file
Explanation: Simply making field separator as .enabled for all the lines here. Then in main program checking condition if 1st field is having --or_` or alphanumeric then print 1st field here.

list tables from mysql dump file with sed

I have situation where I need to extract tables list from mysql dump file. I tried that with sed
sed 's/DROP TABLE.*?`(.*?)`/\1/' baza.sql > 1.txt
but got this error
sed: -e expression #1, char 26: invalid reference \1 on `s' command's RHS
what did I miss in my regexp?
Since you are using Linux, and you need to get all strings inside the first backticks after DROP TABLE, I'd suggest using grep with a PCRE regex like
grep -oP 'DROP TABLE.*?`\K[^`]+' baza.sql > 1.txt
See the PCRE regex demo.
Details
DROP TABLE - some literal text
.*? - any zero or more chars other than line break chars as few as possible
` - a backtick
\K - match reset operator discarding all text matched so far
[^`]+ - one or more chars other than a backtick
If you have matches once per line, you may still go with sed:
sed -nE 's/DROP TABLE.*`([^`]*)`.*/\1/p' baza.sql > 1.txt
Here, -n suppresses default line output, -E enables POSIX ERE (where (...) define a capturing group, not \(...\)), then p flag only prints the result of the substitution.

Find last word in path and remove brackets with regex using bash

I have done lots of searching on SO and google.
I am using a regex tester like Regex 101.
The test string is [../../dvl/namespaces/my-namespace-1/notterminating]
The regex I am using is .*\/([^\/]*)[\[\]']+.
What I am trying to do (returns empty):
$ param="[../../dvl/namespaces/my-namespace-1/notterminating]"
$ echo $param | grep ".*\/([^\/]*)[\[\]']+"
I have tried different options with grep by adding different flags like -o, -e, etc. I have tried "${param}.*\/([^\/]*)[\[\]']+". However, I have been unsuccessful at getting bash to recognize my regex.
You may use sed:
sed -n "s#.*/\([^]['/]*\).*#\1#p" <<< "$param"
See an online demo
Details
.* - matches 0+ chars
/ - / char
\([^]['/]*\) - Group 1: any 0+ chars other than ], [, ' and /
.* - any 0+ chars.
This way, the whole string is replaced with the Group 1 value, and only that value remains as -n suppresses default line output and p prints the result.
Without using any external tool you can do this in pure bash with IFS:
IFS='/\[\]' read -ra arr <<< "$param" && echo "${arr[-1]}"
notterminating
Otherwise you may use this simple sed:
sed 's~.*/~~' <<< "${param//[]]}"
notterminating
Or by using awk:
awk -F '[][/]' '{print $(NF-1)}' <<< "$param"
notterminating
Using sed:
echo "$param" |sed -r -n 's,.*\/(.*)\],\1,p'
output:
notterminating

using sed or grep to extract word

I get only white space when I try to extract the ID by code:
grep "latest" sometextfile | sed 's/.*latest\([\s*]*\).*/\1/'
REPO SAL ID CREATED SIZE
asdasfdg.dshgs.asd:54000/my-thing latest c5521d9803e7 asdfa days ago asdfafd.ad
Code:
grep "latest" sometextfile | sed 's/.*latest\([\s*]*\).*/\1/'
The out put from command above should be the ID: c5521d9803e7.
What is missing at the sed command above?
Probably a simple awk might work:
awk '/latest/{ print $3 }' file
See this online awk demo. It finds a line with latest in it and prints Field 3 contents.
However, following your original logic, you may use sed alone to extract that piece of string after latest:
sed -n '/[[:space:]]latest[[:space:]]/s/.*latest[[:space:]]*\([^[:space:]]*\).*/\1/p' file
See the online demo
Details
/[[:space:]]latest[[:space:]]/ - finds the line with whitespace+latest+whitespace
s/.*latest[[:space:]]*\([^[:space:]]*\).*/\1/p - finds and replaces with Group 1 contents:
.*latest - any 0+ chars up to the last occurrence of latest
[[:space:]]* - 0 or more whitespaces
\([^[:space:]]*\) - Group 1: any 0 or more non-whitespace chars
.* - any 0+ chars to the end of the line
The -n option suppresses line output and p only shows the substitution result.