Split String by Double Back Slashes in Bash - bash

I am trying to split the string by double back slashes in bash and somehow it's not working.
My string is as follow:
abc\\xyz
My Code:
my_str='abc\\xyz'
IFS='\\' read -r -a arr <<< "$my_str"
echo "${#arr[#]}"
I am expecting that the output would be '2' as the length of the array would be 2.
However, I get 3 as a result and when I try to print the array values, I only get 'abc', and the second index remains empty. It seems like something is wrong with my code but I am unable to identify it.
Can anyone please help to resolve the issue?

If there are no spaces in your string you could use bash pattern substitutions to replace pairs of backslashes by a space and assign the result to an indexed array:
$ my_str='abc\\xyz\uvw\\rst\\\012\\\\345'
$ declare -a arr=( ${my_str//\\\\/ } )
$ echo "${#arr[#]}"
5
$ printf '%s\n' "${arr[#]}"
abc
xyz\uvw
rst
\012
345

Perhaps you could try to replace the backslashes on the string fist as showcased in this previous question. However, this would be inefficient for very large strings.

A slightly different take -
$: my_str='abc\\123\\ghi\\\012\\jkl\\foo bar\\\\xyz'
$: IFS='|' read -r -a arr <<< "${my_str//\\\\/\|}"
$: printf "[%s]\n" "${arr[#]}"
[abc]
[123]
[ghi]
[\012]
[jkl]
[foo bar]
[]
[xyz]

Related

How do i add whitepsaces to a String while filling it up in a for-loop in Bash?

Have a string as follows:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
Want to extract the first two directories and save it as another string like this:
"applications/dbt applications/dbt applications/dataform pplications/dataform"
But while filling up the second string, its being saved as
applications/dbtapplications/dbtapplications/dataformapplications/dataform
What i tried:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
arr=($files)
#extracting the first two directories and saving it to a new string
for i in ${arr[#]}; do files2+=$(echo "$i" | cut -d/ -f 1-2); done
echo $files2
files2 echoes the following
applications/dbtapplications/dbtapplications/dataformapplications/dataform
Reusing your code as much as possible:
(assuming to only remove the last right part):
arr=( applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml )
#extracting the first two directories and saving it to a new string
for file in "${arr[#]}"; do
files2+="${file%/*} "
done
echo "$files2"
applications/dbt applications/dbt applications/dataform
You could use a for loop as requested
for dir in ${files};
do file2+=$(printf '%s ' "${dir%/*}")
done
which will give output
$ echo "$file2"
applications/dbt applications/dbt applications/dataform applications/dataform
However, it would be much easier with sed
$ sed -E 's~([^/]*/[^/]*)[^ ]*~\1~g' <<< $files
applications/dbt applications/dbt applications/dataform applications/dataform
Convert the string in an array first. Assuming there are no white/blank/newline space embedded in your strings/path name. Something like
#!/usr/bin/env bash
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
mapfile -t array <<< "${files// /$'\n'}"
Now check the value of the array
declare -p array
Output
declare -a array=([0]="applications/dbt/Dockerfile" [1]="applications/dbt/cloudbuild.yaml" [2]="applications/dataform/Dockerfile" [3]="applications/dataform/cloudbuild.yaml")
Remove all the last / from the path name in the array.
new_array=("${array[#]%/*}")
Check the new value
declare -p new_array
Output
declare -a new_array=([0]="applications/dbt" [1]="applications/dbt" [2]="applications/dataform" [3]="applications/dataform")
Now the value is an array, assign it to a variable or do what ever you like with it. Like what was mentioned in the comment section. Use an array from the start.
Assign the first 2 directories/path in a variable (weird requirement)
new_var="${new_array[#]::2}"
declare -p new_var
Output
declare -- new_var="applications/dbt applications/dbt"

How to get first and second part of a string in bash on last occurrence of a delimiter

I need to split a string on the last occurrence of a delimiter and get both the parts into two variables.
Input could be
- stringOne_One/stringOne_Two/stingOne_Three
- stringTwo_One/stringTwo_Two
I want to split the string on the last occurrence of the delimiter "/" and get both the first and the last part of the string into two variables.
For the first example output should be
var1=stringOne_One/stringOne_Two
var2=stringOne_Three
For the second example, output should be
var1=stringTwo_One
var2=stringTwo_Two
How do I do this in bash. Would prefer a solution using AWK, but any other method is also acceptable.
Use dirname and basename like so:
my_var='stringOne_One/stringOne_Two/stingOne_Three'
var1=$(basename $my_var)
# stingOne_Three
var2=$(dirname $my_var)
# stringOne_One/stringOne_Two
With bash and a regex:
string='stringOne_One/stringOne_Two/stingOne_Three'
[[ "$string" =~ (.*)/(.*) ]]
var1="${BASH_REMATCH[1]}"
var2="${BASH_REMATCH[2]}"
Using parameter expansion:
$ x="stringOne_One/stringOne_Two/stingOne_Three"
$ var1="${x%/*}"
$ var2="${x##*/}"
$ echo "${var1} : ${var2}"
stringOne_One/stringOne_Two : stingOne_Three
$ x="stringTwo_One/stringTwo_Two"
$ var1="${x%/*}"
$ var2="${x##*/}"
$ echo "${var1} : ${var2}"
stringTwo_One : stringTwo_Two

Set bash variable equal to result of string where newlines are replaced by spaces

I have a variable equal to a string, which is a series of key/value pairs separated by newlines.
I want to then replace these newline characters with spaces, and set a new variable equal to the result
From various answers on the internet I've arrived at the following:
#test.txt has the content:
#test=example
#what=s0omething
vars="$(cat ./test.txt)"
formattedVars= $("$vars" | tr '\n' ' ')
echo "$taliskerEnvVars"
Problem is when I try to set formattedVars it tries to execute the second line:
script.sh: line 7: test=example
what=s0omething: command not found
I just want formattedVars to equal test=example what=s0omething
What trick am I missing?
Change your line to:
formattedVars=$(tr '\n' ' ' <<< "$secretsContent")
Notice the space of = in your code, which is not permitted in assignment statements.
I see that you are not setting secretsContent in your code, you are setting vars instead.
If possible, use an array to hold contents of the file:
readarray -t vars < ./test.txt # bash 4
or
# bash 3.x
declare -a vars
while IFS= read -r line; do
vars+=( "$line" )
done < ./test.txt
Then you can do what you need with the array. You can make your space-separated list with
formattedVars="${vars[*]}"
, but consider whether you need to. If the goal is to use them as a pre-command modifier, use, for instance,
"${vars[#]}" my_command arg1 arg2

shell split a string using variable delimiters

My problem is quite simple but I do not manage to solve it:
I have a string that looks like this:
-3445.51692 -7177.16664 -9945.11057
the tricky part is that there could be zero or more withe space between each number and the latter can be either negative or positive, meaning that the string could also be like:
-3445.51692-7177.16664 -9945.11057
or
-3445.51692 7177.16664-9945.11057
(in case of a positive value there is at least one white space that precedes)
and I would like to split this string into three variables that contains each number, e.g.:
a=-3445.51692
b=-7177.16664
c=-9945.11057
Thus, I wanted to use something like
IFS=' -' read -a array <<< "$string"
but I don't know how to specify "zero or more white space". And using "-" as a delimiter removes it from the final result, while I want to keep the sign.
Any ideas ?
Canonicalize the input before you do the IFS splitting, i.e. any minus gets a space prepended:
canonicalized_string=$(echo "$string" | sed 's/-/ -/g')
set -- $canonicalized_string # No need to mess with IFS.
a=$1
b=$2
c=$3
This assumes exactly 3 numbers. In super-compact form:
set -- $(echo "$string" | sed 's/-/ -/g')
a=$1 b=$2 c=$3
Simply use sed to add a space infront of every -, Something like:
echo $string | sed 's/-/ -/g'
You can use read -a by injecting a space first:
s='-3445.51692-7177.16664 -9945.11057'
IFS=' ' read -ra arr <<< "${s//-/ -}"
printf "[%s]\n" "${arr[#]}"
[-3445.51692]
[-7177.16664]
[-9945.11057]

unix shell replace string twice (in one line)

I run a script with the param -A AA/BB . To get an array with AA and BB, i can do this.
INPUT_PARAM=(${AIRLINE_OPTION//-A / }) #get rid of the '-A ' in the begining
LIST=(${AIRLINES_PARAM//\// }) # split by '/'
Can we achieve this in a single line?
Thanks in advance.
One way
IFS=/ read -r -a LIST <<< "${AIRLINE_OPTION//-A /}"
This places the output from the parameter substitution ${AIRLINE_OPTION//-A /} into a "here-string" and uses the bash read built-in to parse this into an array. Splitting by / is achieved by setting the value of IFS to / for the read command.
LIST=( $(IFS=/; for x in ${AIRLINE_OPTION#-A }; do printf "$x "; done) )
This is a portable solution, but if your read supports -a and you don't mind portability then you should go for #1_CR's solution.
With awk, for example, you can create an array and store it in LIST variable:
$ LIST=($(awk -F"[\/ ]" '{print $2,$3}' <<< "-A AA/BB"))
Result:
$ echo ${LIST[0]}
AA
$ echo ${LIST[1]}
BB
Explanation
-F"[\/ ]" defines two possible field separators: a space or a slash /.
'{print $2$3}' prints the 2nd and 3rd fields based on those separators.

Resources