How do we convert a string into N character array and back to string with spaces?
And how do we remove the spaces?
e.g. 123456789 into 2's should give 12 34 56 78 9
Sounds like you don't need the array at all and your final goal is just to insert spaces between groups of two characters. If that's the case you can use
sed 's/../& /g' <<< "your string here"
This will transform your example input 123456789 into the expected output 12 34 56 78 9.
Of course you can assign the result to a variable as usual:
yourVariable="$(sed 's/../& /g' <<< "your string here")"
if needed, how do we remove the spaces?
I'm not sure which spaces you mean. If you are talking about the final result, wouldn't it be easier to use the original input instead of procession the ouput again?
Anyways, you can remove all spaces from a any string using tr -d ' ' <<< "your string" or the parameter substitution ${yourVariable// /}.
$ str='123456789'
$ arr=( $(printf '%s\n' "$str" | sed 's/../& /g') )
$ declare -p arr
declare -a arr=([0]="12" [1]="34" [2]="56" [3]="78" [4]="9")
$ str="${arr[*]}"
$ echo "$str"
12 34 56 78 9
$ str="${str// }"
$ echo "$str"
123456789
$ str=$(printf "%s" "${arr[#]}")
$ echo "$str"
123456789
If you need to split a string into array, you can use IFS variable:
IFS=' '
arr=( )
read -r -a arr <<< "string morestring another"
unset IFS
To remove spaces from string you can use different approaches, one of them is:
str="123 123 12312312"
echo ${str// /}
//output: 12312312312312
This would be demonstrative to your question.
Convert a string into N character array:
string="0123456789abcdefghijklmnopqrstuvwxyz"
number_max_of_char_per_set=4
increment_by=$number_max_of_char_per_set
for i in `seq 0 $increment_by ${#string}`
#for i in $(seq 0 ${#string})
do array[$i]=${string:$i:number_max_of_char_per_set}
done
... back to string with spaces:
string_new=${array[#]}
echo "zero element of array is [${array[0]}]"
echo "entire array is [${array[#]}]"
echo $string_new
remove the spaces:
string_new=${string_new//[[:space:]]/}
echo $string_new
Related
In Bash, given an associative array, how do I find the length of the longest key?
Say, I declare myArray as shown below:
$ declare -A myArray=([zero]=nothing [one]='just one' [multiple]='many many')
$ echo ${myArray[zero]}
nothing
$ echo ${myArray[one]}
just one
$ echo ${myArray[multiple]}
many many
$
I can get it using the below one-liner
$ vSpacePad=`for keys in "${!myArray[#]}"; do echo $keys; done | awk '{print length, $0}' | sort -nr | head -1 | awk '{print $1}'`;
$ echo $vSpacePad
8
$
Am looking for something simpler like below, but unfortunately, these just give the count of items in the array.
$ echo "${#myArray[#]}"
3
$ echo "${#myArray[*]}"
3
You do need to loop over the keys, but you don't need any external tools:
max=-1
for key in "${!myArray[#]}"; do
len=${#key}
((len > max)) && max=$len
done
echo $max # => 8
If you want to print the elements of an array one per line, you don't need a loop: printf is your friend:
printf '%s\n' "${!myArray[#]}"
printf iterates until all the arguments are consumed
Is there a built-in way to get the maximum length of the keys in an associative array
No.
how do I find the length of the longest key?
Iterate over array elements, get the element length and keep the biggest number.
Do not use backticks `. Use $(..) instead.
Quote variable expansions - don't echo $keys, do echo "$keys". Prefer printf to echo.
If array elements do not have newlines and other fishy characters, you could:
printf "%s\n" "${myArray[#]}" | wc -L
Due to the limitation of 9 parameters in a script, my objective is to pass about 30 strings bundled in an array from calling script (scriptA) to called script (scriptB).
My scriptA looks something like this...
#!/bin/bash
declare -a arr=( ab "c d" 123 "string with spaces" 456 )
. ./scriptB.sh "Task Name" "${arr[#]}"
My scriptB looks something like this...
#!/bin/bash
arg1="$1"
shift
arg2=("$#")
read -a arr1 <<< "$#"
j=0
for i in "${arr1[#]}"; do
#echo ${arr1[j]}
((j++))
case "$j" in
"1")
param1="${i//(}"
echo "$j=$param1"
;;
"2")
param2="${i}"
echo "$j=$param2"
;;
"3")
param3="${i}"
echo "$j=$param3"
;;
"4")
param4="${i}"
echo "$j=$param4"
;;
"5")
param5="${i//)}"
echo "$j=$param5"
;;
esac
done
OUTPUT:
1=ab
2=c
3=d
4=123
5=string
Problem:
1. I see parenthesis ( and ) gets added to the string which I have to strip them out
2. I see an array element (with spaces) though quoted under double quotes get to interpreted as separate elements by spaces.
read -a arr1 <<< "$#"
is wrong. The "$#" here is equal to "$*", and then read will split the input on whitespaces (spaces, tabs and newlines) and also interpret \ slashes and assign the result to array arr1. Remember to use read -r.
Do:
arr1=("$#")
to assign to an array. Then you could print with:
for ((i=1;i<${#arr1};++i)); do
printf "%d=%s\n" "$i" "${arr1[$i]}"
done
of 9 parameters in a script, my objective is to pass about 30 strings bundled in an array from calling script (scriptA)
Ok. But "${arr[#]}" is passing multiple arguments anyway. If you want to pass array as string, pass it as a string (note that eval is evil):
arr=( ab "c d" 123 "string with spaces" 456 )
./scriptB.sh "Task Name" "$(declare -p arr)"
# Then inside scriptB.sh, re-evaulate parameter 2:
eval "$2" # assigns to arr
Note that the scriptB.sh is sourced in your example, so passing arguments.... makes no sense anyway.
I see an array element (with spaces) though quoted under double quotes get to interpreted as separate elements by spaces
Yes, because you interpreted the content with read, which splits the input on characters in IFS, which by default is set to space, tab and newline. You could print arguments on separate lines and change IFS accordingly:
IFS=$'\n' read -r -a arr1 < <(printf "%s\n" "$#")
or even use a zero terminated string:
mapfile -t -d '' arr1 < <(printf "%s\0" "$#")
but those are just fancy and useless ways of writing arr1=("$#").
Note that in your code snipped, arg2 is an array.
I have a line which has space separated strings:
line="hello 3] 5c 100 memory"
I want to split this string into an integer array, so that the following:
echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}
Outputs 3 5 100
Can someone help, please?
With Bash's Parameter expansion:
line="hello 3] 5c 100 memory"
arr=(${line//[^0-9 ]/}) # replace everything but 0-9 and space with nothing
declare -p arr
Output:
declare -a arr='([0]="3" [1]="5" [2]="100")'
I am looking to append each element of a bash array with | except the last one.
array=("element1" "element2" "element3")
My desired output would be
array=("element1"|"element2"|"element3")
What I have done
for i in ${!array[#]};
do
array1+=( "${array[$i]}|" )
done
Followed by
array=echo ${array1[#]}|sed 's/|$//'
Is there any other looping approach I can use that only appends the character until the last but one element?
For your special case, where only one single character has to be inserted between each array member, the simplest solution is probably to use array expansion (and changing the separation character IFS according to your need beforehand):
$ array=("element1" "element2" "element3")
$ array=( $( IFS="|" ; echo "${array[*]}") )
$ echo "\$array[0] is '${array[0]}'"
$array[0] is 'element1|element2|element3'
You can use:
# append | all except last element
read -ra array < <( printf "%s|" "${array[#]:0:$((${#array[#]} - 1))}"; echo "${array[#]: -1}"; )
# Check array content now
declare -p array
declare -a array='([0]="element1|element2|element3")'
"${array[#]:0:$((${#array[#]} - 1))}" will get all but last element of array
"${array[#]: -1}" will get the last element of array
printf "%s|" will append | in front of all the arguments
< <(...) is process substitution to read output of any command from stdin
read -ra will read the input in an array
$ array=("element1" "element2" "element3")
$ printf -v str "|%s" "${array[#]}"
$ array=("${str:1}")
$ declare -p array
declare -a array='([0]="element1|element2|element3")'
The printf statement creates a string str that contains |element1|element2|element3, i.e., one more | than we want (at the beginning).
The next statement uses substring parameter expansion, ${str:1}, to skip the first character and reassigns to array, which now consists of a single element.
A simple solution (if you don't mind changing IFS) is:
$ array=("element1" "element2" "element3")
$ IFS="|"; printf "%s\n" "${array[*]}"
And to re-assign to the variable array (which doesn't change IFS):
$ array=("$(IFS="|"; printf "%s\n" "${array[*]}")")
$ printf '%s\n' "${array[#]}"
element1|element2|element3
An alternative solution is:
$ array=($(printf '%s|' "${array[#]}")); array="${array%?}"
$ printf '%s\n' "${array}"
A more complex solution (script) is:
array=("element1" "element2" "element3")
delimiter='|'
unset newarr
for val in "${array[#]}"
do newarr=$newarr${newarr+"$delimiter"}$val
done
array=("$newarr")
echo "array=($array)"
I want to create a bash array from a NUL separated input (from stdin).
Here's an example:
## Let define this for clarity
$ hd() { hexdump -v -e '/1 "%02X "'; echo ;}
$ echo -en "A B\0C\nD\0E\0" | hd
41 20 42 00 43 0A 44 00 45 00
So this is my input.
Now, working with NUL works fine if not using the -a of read command:
$ while read -r -d '' v; do echo -n "$v" | hd; done < <(echo -en "A B\0C\nD\0E\0")
41 20 42
43 0A 44
45
We get the correct values. But I can't store these values using -a:
$ read -r -d '' -a arr < <(echo -en "A B\0C\nD\0E\0")
$ declare -p arr
declare -a arr='([0]="A" [1]="B")'
Which is obviously not what I wanted. I would like to have:
$ declare -p arr
declare -a arr='([0]="A B" [1]="C
D" [2]="E")'
Is there a way to go with read -a, and if it doesn't work, why? Do you know a simple way to do this (avoiding the while loop) ?
read -a is the wrong tool for the job, as you've noticed; it only supports non-NUL delimiters. The appropriate technique is given in BashFAQ #1:
arr=()
while IFS= read -r -d '' entry; do
arr+=( "$entry" )
done
In terms of why read -d '' -a is the wrong tool: -d gives read an argument to use to determine when to stop reading entirely, rather than when to stop reading a single element.
Consider:
while IFS=$'\t' read -d $'\n' words; do
...
done
...this will read words separated by tab characters, until it reaches a newline. Thus, even with read -a, using -d '' will read until it reaches a NUL.
What you want, to read until no more content is available and split by NULs, is not a '-d' of NUL, but no end-of-line character at all (and an empty IFS). This is not something read's usage currently makes available.
bash-4.4-alpha added a -d option to mapfile:
The `mapfile' builtin now has a -d option to use an arbitrary
character
as the record delimiter, and a -t option to strip the delimiter as
supplied with -d.
— https://tiswww.case.edu/php/chet/bash/CHANGES
Using this, we can simply write:
mapfile -t -d '' arr < <(echo -en "A B\0C\nD\0E\0")
If anyone wonders, here's the function (using while) that I use to store values from a NUL-separated stdin:
read_array () {
local i
var="$1"
i=0
while read -r -d '' value; do
printf -v "$var[$i]" "%s" "$value"
i=$[$i + 1]
done
}
It can then be used quite cleanly:
$ read_array arr < <(echo -en "A B\0C\nD\0E\0")
$ declare -p arr
declare -a arr='([0]="A B" [1]="C
D" [2]="E")'
Here's a simplification of #vaab's function. It uses bash 4.3's nameref feature:
read_array () {
local -n a=$1
while read -r -d '' value; do
a+=("$value")
done
}
Test:
test_it () {
local -a arr
read_array arr < <(echo -en "A B\0C\nD\0E\0")
declare -p arr
}
test_it