bash script string manipulation with list - bash

I want to generate a comma separated ip values with mapped ports and create a string.
Here is my code:
zk_ip="['192.168.0.10', '192.168.0.20', '192.168.0.30']"
zk_host=""
for i in $zk_ip[#]
do
add=$(echo "$i:2181")
zk_host="$zk_host $add"
done
echo $zk_host
Output:
[192.168.0.10,:2181 192.168.0.20, :2181 192.168.0.30]:2181
Expected ouptut:
192.168.0.10:2181, 192.168.0.20:2181, 192.168.0.30:2181

So, you have a JSON-ish array that you want to modify (JSON strings are enclosed in double quotes).
I would use a JSON parser to manage this: jq
zk_ip="['192.168.0.10', '192.168.0.20', '192.168.0.30']"
new_ip=$(echo "$zk_ip" | tr "'" '"' | jq -c 'map("\(.):2181")')
echo "$new_ip"
["192.168.0.10:2181","192.168.0.20:2181","192.168.0.30:2181"]
If you want the output to not look like JSON, you can do:
new_ip=$(echo "$zk_ip" | tr "'" '"' | jq -r 'map("\(.):2181") | join(", ")')
echo "$new_ip"
192.168.0.10:2181, 192.168.0.20:2181, 192.168.0.30:2181

You may use:
zk_ip="['192.168.0.10', '192.168.0.20', '192.168.0.30']"
zk_host=""
for i in ${zk_ip//[][,\']/}; do
zk_host+="$i:2181, "
done
echo "${zk_host%, }"
192.168.0.10:2181, 192.168.0.20:2181, 192.168.0.30:2181

Assuming that you have your IP addresses in an array, such as
zk_ip=( '192.168.0.10' '192.168.0.20' '192.168.0.30' )
then,
( IFS=','; printf '%s\n' "${zk_ip[*]/%/:2181}" )
would print
192.168.0.10:2181,192.168.0.20:2181,192.168.0.30:2181
Setting IFS makes "${zk_ip[*]}" expand to a comma-delimited string with all the entries of the array. With /%/:2181 each element is suffixed with the string :2181 before printing.

Related

How to convert this string to an interable list of Python files in Bash?

I have a string as follows:
string = '[ "file1.py", "file2.py", "file3.py", "file4.py" ]'
How to convert it to an iterable list of individual filenames so that I can run a for loop on it as follows:
for filen in fileArr
do
echo $filen
done
Expected output:
file1.py
file2.py
file3.py
file4.py
So far I have just removed the first and last square brackets using string=${string:1:${#string}-2} but I still have quotations and commas to remove. Is there a clean and simple way to achieve this?
You can use the tr command (more information about the tr command here) to eliminate the start bracket, the end bracket, and the double quotations. Also, you can substitute the coma for a tab so you can later iterate the over the result.
Here the code:
$ s='[ "file1.py", "file2.py", "file3.py", "file4.py" ]'
$ s2=$(echo $s | tr "[" " " | tr "]" " " | tr "\"" " " | tr "," "\\t")
$ for x in $s2; do echo $x ; done
file1.py
file2.py
file3.py
file4.py
More in-depth, the s2 statement uses:
s2=$( --> Assigns the output of the full command to s2
echo $s --> Prints the content of s to the pipes
| tr "[" " " --> Substitute the start bracket by spaces
| tr "]" " " --> Substitute the end bracket by spaces
| tr "\"" " " --> Substitute the double quotations by spaces
| tr "," "\\t" --> Substitute the comma by a tab
)
Notice that this code is valid for the kind of input you provided but it will not work if the filenames contain spaces.
EDIT:
Another solution is possible using substring replacement instead of using the tr command.
Here the code:
$ s2=${s//\[/} # Erase the start brackets
$ s3=${s2//\]/} # Erase the end brackets
$ s4=${s3//\"/} # Erase the double quotations
$ s5=${s4//,/ } # Substitute the comma by a tab
$ for x in $s5; do echo $x ; done
file1.py
file2.py
file3.py
file4.py
As the previous solution, notice that the code will not work if the filenames contain spaces (since they will be considered separated entries).
EDIT 2:
As #Z4-tier pointed out, the first option can be re-written in a more compact way using the -d option available in tr. This option erases the given characters. Also, the obtained string after the parsing might not be iterable if the Internal Field Separator (IFS) is set incorrectly. Although I think that the previous solutions cover most of the cases, you might consider setting and restoring the IFS value if it was set to something else than the default value.
Hence, you could write:
$ s='[ "file1.py", "file2.py", "file3.py", "file4.py" ]'
$ s2=$(echo $s | tr -d "[]\"" | tr "," "\\t")
$ IFS=$' '
$ for x in $s2; do echo $x ; done
file1.py
file2.py
file3.py
file4.py
$ IFS= #restore your IFS value
tr <string1> <string2> will replace any occurance of a character in string1 with the character appearing in the same index position from string2, so this can be done with 1 pipe.
tr can also be used as tr -d <string1> where any occurance of a character in string1 is deleted.
s='[ "file1.py", "file2.py", "file3.py", "file4.py" ]'
s2=$(echo $s | tr -d '[]",')
This is not iterable like you might think. It is still one string:
bash-$ echo "${s2[0]}"
file1.py file2.py file3.py file4.py
Try this:
IFS=$' '
bash-$ my_array=($(echo $s | tr -d '[]",'))
bash-$ echo "${my_array[0]}"
file1.py
bash-$ for k in "${my_array[#]}"; do echo $k; done
file1.py
file2.py
file3.py
file4.py
This sets IFS (the internal field separator) to a space character to take advantage of the spaces that are left in the string after it gets run through tr. It uses a subshell to translate s into what we called s2 above (now using my_array to indicate that it's not a string...), surrounds it with (...) to create an indexed array (this is where IFS is important).

Extracting a substring

I have to find a substring where my string starts with country=" and ends with " like following-
country="NZ"
I have to extract only NZ part and add it to an existing string like-
string+=NZ
Please helP!!!
Use sed in regular expression mode:
string=""
INPUT='country="NZ"'
string+=$(echo $INPUT | sed -r 's/country="(.*?)"/\1/')
If your string truly only contains country="CODE", then cut works too, using " as the delimiter:
echo 'country="NZ"' | cut -d\" -f2

to print words seperated with special charecters in shell script

shell script to print three words differently I have tried
{
a="Uname/pass#last"
echo $a | tr "/" "\n" | tr "#" "\n"
output is:
Uname
pass
last
}
I want it as
{Username- Uname
Password- pass
lastname-last}
Ok, I guess you want to add a prefix to each results:
printf 'Username\nPassword\nlastname' > /tmp/prefixes
a="Uname/pass#last"
echo "${a}" | tr '/#' '\n\n' | paste -d':' /tmp/prefixes -
ie: paste together the output of /tmp/prefixes and of the Standard Input (-), which is receiving the output of : echo ".../...#..." | tr '/#' '\n\n'
(and in the resulting output, separate the 2 with a : in this example, or whatever else you would want. Ex: - like in your question.)
and it outputs :
Username:user
Password:pass
lastname:last
(I know you wanted a - instead of a : but I give my example with : to better separate the "-" denoting the standard input, and the ":" denoting the field-separator character in the output. Just change -d':' into -d'-' to have a - instead.)
First off, I hope you're not going to manipulate important passwords in a shell script and external commands. There are some risks involved with that.
Defining the problem
I suspect you want split a string encoding a user's Username, password and surname into a three line structure, adding tags to document which is which. For that, tr is insufficient.
However, it can be done inside the shell.
Example (bash, ksh):
function split_account_string {
typeset account=${1:?account string} uname pass last t
uname=${account%%/*}
last=${account##*#}
t=${account#$uname/}
pass=${t%#*}
[[ $uname/$pass#$last == "$account" ]] || return
echo "{Username-$uname"
echo "Password-$pass"
echo "lastname-$last}"
}
split_account_string "USER_A/seKreT#John.Doe"
This function will extract all tokens between the first / and the last # as the value of the password. If either one is missing, it will print nothing, and return an error status.
When run, this gives:
{Username-USER_A
Password-seKreT
lastname-John.Doe}
Use this simple script and get the output.
#!/bin/bash
a="Uname/pass#last"
array2=(`echo $a | tr "/" "\n" | tr "#" "\n"`)
array1=(`echo -e "Username\nPassword\nlastname"`)
i=${#array1[#]}
for (( j=0 ; j<$i ; j++ ))
do
echo "${array1[$j]}=${array2[$j]}"
done

String manipulation required

Here is a sample string . I would like to get the output from this in the specified format.
String:
/vob/TEST/.##/main/ch_vobsweb/1/VOBSWeb/main/ch_vobsweb/4/VobsWebUI/main/ch_vobsweb/2/VaultWeb/main/ch_vobsweb/2/func.js
filename;path to file
func.js;VOBSWeb/VosWebUI/VaultWeb/func.js
The filename is listed at the end of the whole string , and it's path is supposed to be stripped using the characters after each numeric value (eg. /1/VOBSWeb/ and then /4/VobsWebUI and then /2/vaultWeb)
one way
$ string="/vob/TEST/.##/main/ch_vobsweb/1/VOBSWeb/main/ch_vobsweb/4/VobsWebUI/main/ch_vobsweb/2/VaultWeb/main/ch_vobsweb/2/func.js"
$ path=$(echo "$string" | sed "s|\/[0-9]\/|\n|g"|sed 's|\/.*||' | tr "\n" "/"|sed 's/\/$//')
$ echo ${path##*/}
func.js
$ echo ${path%\/*}
/VOBSWeb/VobsWebUI/VaultWeb

Bash script: regexp reading numerical parameters from text file

Greetings!
I have a text file with parameter set as follows:
NameOfParameter Value1 Value2 Value3 ...
...
I want to find needed parameter by its NameOfParameter using regexp pattern and return a selected Value to my Bash script.
I tried to do this with grep, but it returns a whole line instead of Value.
Could you help me to find as approach please?
It was not clear if you want all the values together or only one specific one. In either case, use the power of cut command to cut the columns you want from a file (-f 2- will cut columns 2 and on (so everything except parameter name; -d " " will ensure that the columns are considered to be space-separated as opposed to default tab-separated)
egrep '^NameOfParameter ' your_file | cut -f 2- -d " "
Bash:
values=($(grep '^NameofParameter '))
echo ${values[0]} # NameofParameter
echo ${values[1]} # Value1
echo ${values[2]} # Value2
# etc.
for value in ${values[#:1]} # iterate over values, skipping NameofParameter
do
echo "$value"
done

Resources