How to extract a value of a key from a long string with sed? [duplicate] - linux

This question already has answers here:
Can I use sed to manipulate a variable in bash?
(3 answers)
Closed 4 years ago.
I have a long string and from that string I want to extract a value of a key and store it in a variable. I want to extract value of userName from abc string. I tried below code but it say's file name too long error.
abc="Create [newSystem=System [identityDomain=bbundlesystemser201201-test, admin=AdminUser [firstName=BSystemAdminGivenName, middleName=null, lastName=BSystemAdminFalilyName, userName=bbundlesystemadminusername, password=*******, email=hello#example.com], idmConsoleURL=https://abc.com.jspx, sftpHost=d910.abc.com, sftpUser=3pyylzoo, sftpPwd=*******]]"
echo $abc
sed -n 's/^userName= //p' "$abc"
Is there anything wrong I am doing? I want to extract value of userName and store it in a variable.
userName=bbundlesystemadminusername

You can use BASH regex matching:
[[ $abc =~ userName=([^][,[:space:]]+) ]] && userName="${BASH_REMATCH[1]}"
echo "$userName"
bbundlesystemadminusername
Or else, you can use this sed command:
userName=$(sed 's/.*userName=\([^][,[:space:]]*\).*/\1/' <<< "$abc")

I think I'd do this with an associative array and process substitution in bash 4:
$ declare -A a
$ while IFS== read k v; do a["$k"]="$v"; done < <(grep -oEi '[a-z]+=[^], ]+' <<<"$abc")
$ printf '%q\n' "${a[userName]}"
bbundlesystemadminusername
While this doesn't properly respect the data structure of your input variable, it does recognize key=value pairs and save them in an easily accessible array, using only a single grep -o to split the string into the KV pairs. The nice this about this is of course that you've got the rest of the data also available to you, should you want to avoid unnecessary calls to grep or awk or sed or whatever.
Note that associative arrays were added to bash in version 4. If you're doing this in macOS (or indeed in a POSIX shell), it'll have to be adjusted.

Related

How to reuse an ANSI-C quoting variable in another Bash command?

I'm trying to figure out how to use a variable containing an ANSI-C quoting string as an argument for a subsequent bash command.
The string in the variable itself is a list of files (can virtually be a list of anything).
For example, I have a file containing a list of other files, for example test.lst containing :
>$ cat test.lst
a.txt
b.txt
c.txt
I need to pass the file content as a single string so I'm doing :
test_str=$(cat test.lst)
then converts to ANSI-C quoting string:
test_str=${test_str#Q}
So at the end I have :
>$ test_str=$(cat test.lst)
>$ test_str=${test_str#Q}
>$ echo $test_str
$'a.txt\nb.txt\nc.txt'
which is what I'm looking for.
Then problem arises when I try to reuse this variable as a string list in another bash command.
For example direct use into a for loop :
>$ for str in $test_str; do echo $str; done
$'a.txt\nb.txt\nc.txt'
What I expect at this step is that it prints the same thing as the content of the original test.lst
I also tried expanding it back but it leaves leading $' and trailing '
>$ str=${test_str#E}
>$ echo $str
$'a.txt b.txt c.txt'
I also tried printf and some other stuffs to no avail. What is the correct way to use such ANSI-C quoting variable into a bash command ?
How about:
eval echo "${test_str}"
I believe that an ANSI-C quoted string is meant to be evaluated by bash command line parser.
In the first place, why do you need quoting? Just keep the data untouched stored as elements of an array:
mapfile -t filelist < test.lst
# iterate through the list
for file in "${filelist[#]}"; do printf '%s\n' "$file"; done

Read first characters of a variable in bash script? [duplicate]

This question already has answers here:
In Bash, how can I check if a string begins with some value?
(13 answers)
Closed 6 years ago.
I have a file where i get some informations through a bash script to put data in a DB table and i'd like to know how to read the first characters of a variable because if it starts with "CE-" that line's data will go into a table if not they must be inserted in an other one, how can i do this?
Like this-
var=CE-xxxxx
echo "$var"
output- CE-xxxxx
var2=$(echo "$var" | cut -c 1-3)
echo "$var2"
output- CE-
Then you can check if $var2 matches your criteria and use it further.
You can use cut to get the bytes that you need:
V="CE-IMPORTANT"
I=$(echo $V | cut -b 4-)
If you want to use the - as separator:
I=$(echo $V | cut -d '-' -f 2)
In both cases you get "IMPORTANT" in I var

Simple bash get part of a string

I have this text and more stored in a $hello:
"id":"1234"
How can I find id, and then get just the number?
There will be a lot of other text in the string.
Whole string is:
After making says:
{"self":"http://xxxxx:8081/rest/api/latest/version/1234","id":"1234","description":"desc","name":"vi1teast","archived":false,"released":false,"releaseDate":"2016-02-06","overdue":true,"userReleaseDate":"06/Feb/16","projectId":xxxxx}
I cannot use any JSON parsers, must be with string manipulation!
If you want to parse a JSON, use jq!
$ jq '.id' file
"1234"
And if you don't want to get the quotes, use -r:
$ jq -r '.id' file
1234
From the manual:
--raw-output / -r:
With this option, if the filter’s result is a string then it will be
written directly to standard output rather than being formatted as a
JSON string with quotes. This can be useful for making jq filters talk
to non-JSON-based systems.
Note I had to tweak your JSON and write "projectId":"xxxxx" so that it is a valid one.
If you just want to get the id and can make sure that it will only contain digits, you can use a pure bash solution like this. It will use bash regex and BASH_REMATCH for referencing the capturing groups.
#!/bin/bash
str='{"self":"http://xxxxx:8081/rest/api/latest/version/1234","id":"1234","description":"desc","name":"vi1teast","archived":false,"released":false,"releaseDate":"2016-02-06","overdue":true,"userReleaseDate":"06/Feb/16","projectId":xxxxx}'
if [[ "$str" =~ ^.*\"id\":\"([0-9]*)\".*$ ]];
then
echo ${BASH_REMATCH[1]} ;
else
echo "Not propper format";
fi
This will output the desired 1234.
You can also shorten it up:
#short version
[[ "$str" =~ ^.*\"id\":\"([0-9]*)\".*$ ]] && id=${BASH_REMATCH[1]}
echo $id
If you cannot use a parser, try this sed:
$ sed 's/.*"id":"\([0-9]\+\)".*/\1/' <<< "$hello"
1234

In shell, split a portion of a string with dot as delimiter [duplicate]

This question already has answers here:
How do I split a string on a delimiter in Bash?
(37 answers)
Closed 4 years ago.
I am new to shell scripting, can you please help with below requirement, thanks.
$AU_NAME=AU_MSM3-3.7-00.01.02.03
#separate the string after last "-", with "." as delimiter
#that is, separate "00.01.02.03" and print/save as below.
major=00
minor=01
micro=02
build=03
First, note that you don't use $ when assigning to a parameter in the shell. Your first line should be just this:
AU_NAME=AU_MSM3-3.7-00.01.02.03
The $ is used to get the value of the parameter once assigned. And the bit after the $ can be an expression in curly braces with extra stuff besides just the name, allowing you to perform various operations on the value. For example, you can do something like this:
IFS=. read major minor micro build <<EOF
${AU_NAME##*-}
EOF
where the ##*- strips off everything from the beginning of the string through the last '-', leaving just "00.01.02.03", and the IFS (Internal Field Separator) parameter tells the shell where to break the string into fields.
In bash, zsh, and ksh93+, you can get that onto one line by shortening the here-document to a here-string:
IFS=. read major minor micro build <<<"${AU_NAME##*-}"
More generally, in those same shells, you can split into an arbitrarily-sized array instead of distinct variables:
IFS=. components=(${AU_NAME##*-})
(Though that syntax won't work in especially-ancient versions of ksh; in them you have to do this instead:
IFS=. set -A components ${AU_NAME##*-}
)
That gets you this equivalence (except in zsh, which by default numbers the elements 1-4 instead of 0-3):
major=${components[0]}
minor=${components[1]}
micro=${components[2]}
build=${components[3]}
In bash, you can do something like this:
version=$(echo $AU_NAME | grep -o '[^-]*$')
major=$(echo $version | cut -d. -f1)
minor=$(echo $version | cut -d. -f2)
micro=$(echo $version | cut -d. -f3)
build=$(echo $version | cut -d. -f4)
The grep call uses -o which outputs only the matching part of the line. The match itself is every non-hyphen character to the end of the line.
The cut command uses the delimeter . (-d.), and uses -f to select individual fields.
It's a little clunky. I'm sure there are probably better ways to achieve this, but you can do quite a lot with grep and cut alone so they're handy tools to have in your arsenal.
You can use parameter expansion and the special IFS variable.
#! /bin/bash
AU_NAME=AU_MSM3-3.7-00.01.02.03
IFS=. VER=(${AU_NAME##*-})
for i in {0..3} ; do
echo ${VER[i]}
done
major=${VER[0]}
minor=${VER[1]}
micro=${VER[2]}
build=${VER[3]}
BTW, in an assignment, do not start the variable on the left hand side with a dollar sign.

How can I convert a string from uppercase to lowercase in Bash? [duplicate]

This question already has answers here:
How to convert a string to lower case in Bash
(29 answers)
Closed 4 years ago.
I have been searching to find a way to convert a string value from uppercase to lowercase. All the search results show approaches of using the tr command.
The problem with the tr command is that I am able to get the result only when I use the command with the echo statement. For example:
y="HELLO"
echo $y| tr '[:upper:]' '[:lower:]'
The above works and results in 'hello', but I need to assign the result to a variable as below:
y="HELLO"
val=$y| tr '[:upper:]' '[:lower:]'
string=$val world
When assigning the value like above it gives me an empty result.
PS: My Bash version is 3.1.17
If you are using Bash 4, you can use the following approach:
x="HELLO"
echo $x # HELLO
y=${x,,}
echo $y # hello
z=${y^^}
echo $z # HELLO
Use only one , or ^ to make the first letter lowercase or uppercase.
One way to implement your code is
y="HELLO"
val=$(echo "$y" | tr '[:upper:]' '[:lower:]')
string="$val world"
This uses $(...) notation to capture the output of the command in a variable. Note also the quotation marks around the string variable -- you need them there to indicate that $val and world are a single thing to be assigned to string.
If you have Bash 4.0 or higher, a more efficient & elegant way to do it is to use Bash built-in string manipulation:
y="HELLO"
string="${y,,} world"
Note that tr can only handle plain ASCII, making any tr-based solution fail when facing international characters.
Same goes for the Bash 4-based ${x,,} solution.
The AWK tool, on the other hand, properly supports even UTF-8 / multibyte input.
y="HELLO"
val=$(echo "$y" | awk '{print tolower($0)}')
string="$val world"
Answer courtesy of liborw.
Execute in backticks:
x=`echo "$y" | tr '[:upper:]' '[:lower:]'`
This assigns the result of the command in backticks to the variable x. (I.e., it's not particular to tr, but it is a common pattern/solution for shell scripting.)
You can use $(..) instead of the backticks. See here for more info.
I'm on Ubuntu 14.04 (Trusty Tahr), with Bash version 4.3.11. However, I still don't have the fun built-in string manipulation ${y,,}
This is what I used in my script to force capitalization:
CAPITALIZED=`echo "${y}" | tr '[a-z]' '[A-Z]'`
If you define your variable using declare (old: typeset) then you can state the case of the value throughout the variable's use.
declare -u FOO=AbCxxx
echo $FOO
Output:
ABCXXX
Option -l to declare does lowercase:
When the variable is assigned a value, all upper-case characters are converted to lower-case. The upper-case attribute is disabled.
Building on Rody's answer, this worked for me.
y="HELLO"
val=$(echo $y | tr '[:upper:]' '[:lower:]')
string="$val world"
One small modification: if you are using underscore next to the variable, you need to encapsulate the variable name in {}.
string="${val}_world"

Resources