I got the following code. I would like to make cc dd ee ff as array [2]
keyvariable="aa bb cc dd ee ff"
while read -a line;
do
a=$(echo "${line[0]}")
b=$(echo "${line[1]}")
c=$(echo "${line[2]}")
done <<< "$keyvariable"
echo "$a $b $c"
current output:
aa bb cc
I would like to have the following output, where aa is [0] bb is [1] and cc dd ee is [2]
aa bb cc dd ee
You don't need the while loop here at all.
You don't want to use read with the -a switch at all here. Instead you want:
read -r a b c <<< "$keyvariable"
In this case, read will split the (first line of the) expansion of the variable keyvariable on the spaces, but only for the first and second fields (these will go in variables a and b) and the remaining part will go in c. The -r switch is used in case you have backslashes in your string; without this, backslashes would be treated as an escape character.
gniourf_gniourf's answer is absolutely correct, however if you don't know how many fields you are going to have or need to select your "prefix" fields based on some other criteria then using an array and Substring Expansion (which is a bad name for this usage of it but that's what it is called) can let you do that.
$ keyvariable="aa bb cc dd ee ff"
$ read -a line <<<"$keyvariable"
$ a=${line[0]}
$ b=${line[1]}
$ c=${line[#]:2}
$ echo "$a"
aa
$ echo "$b"
bb
$ echo "$c"
cc dd ee ff
Also note the lack of $(echo ...) on the assignment lines. You don't need it.
Just do
a=( $keyvariable )
and you have array a with your values, so your example would be
keyvariable="aa bb cc dd ee ff"
line=( $keyvariable ) # of course you can do it in the same line
a="${line[0]}"
b="${line[1]}"
n=$(( ${#line[#]} - 1))
unset line[$n]
echo "$a $b $c"
Related
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
With regards to this question (Put line seperated grep result into array), when I use
echo v1.33.4 | arr=($(egrep -o '[0-9]{1,3}'))
with GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin18.2.0) on macOS
I get have an empty array arr in return for
echo "($arr)"
()
then the expected output
1
33
4
What do I do wrong here?
It wouldn't work with the syntax you have. You are not populating the array with the result of grep. You are not handling the string passed over the pipe and populating an empty array at the received end of the pipe.
Perhaps you were intending to do
array=($(echo v1.33.4 | egrep -o '[0-9]{1,3}'))
Notice how the echo of the string is passed over to the standard input of egrep which was missing in your attempt.
But as in the linked answer, using mapfile would be the best option here because with the above approach if the search results contains words containing spaces, they would be stored in separated indices in the array rather than in a single one.
mapfile -t array < <(echo "v1.33.4" | egrep -o '[0-9]{1,3}')
printf '%s\n' "${array[#]}"
Notice the array expansion in bash takes the syntax of "${array[#]}" and not just a simple "${array}" expansion.
Messed with it a bit and this seems to work:
$ arr=$(echo "v1.33.4" | egrep -o '[0-9]{1,3}')
$ echo $arr
1 33 4
$ echo "($arr)"
(1
33
4)
I am trying to store the variables $d, $tf_name, $db_orig created in the following loop to a file.I want to end up with a tab separated MY_FILE.txt containing the following fields $d, $tf_name, $db_orig and each iteration of this set of variables to be stored in a new line in the file MY_FILE.txt.
MY_ARRAY=()
for d in */
do
IN=$d
folderIN=(${IN//_/ })
tf_name=${folderIN[-1]%/*}
db_orig=${folderIN[-2]%/*};
ENTRY="$d\t$tf\t$id\t$db_orig\n"
MY_ARRAY+=$ENTRY
done
$MY_ARRAY > MY_FILE.txt
It doesn't recognise \t and \n as TAB and NEWLINE respectively. It stores all the values next to each other in the same line without TAB, in the array MY_ARRAY.
Any help?
Yes, this happens because $MY_ARRAY > MY_FILE.txt is not a valid command.
You need to print your array to the file.
And in order to print it correctly you need either to use
echo -e "${MY_ARRAY[#]}" >file or printf
By man echo
echo -e : enable interpretation of backslash escapes
Moreover, if you need to store the $ENTRY to your array you need to do it like this:
MY_ARRAY+=("$ENTRY")
In any case, you can do it without the need of an array. You can just apply += in the ENTRY : ENTRY+="$d\t$tf\t$id\t$db_orig\n"
Test:
$ e+="a\tb\tc\td\n"
$ e+="aa\tbb\tcc\tdd\n"
$ e+="aaa\tbbb\tccc\tddd\n"
$ echo -e "$e"
a b c d
aa bb cc dd
aaa bbb ccc ddd
# Test with array
$ e="a\tb\tc\td\n" && myar+=("$e")
$ e="aa\tbb\tcc\tdd\n" && myar+=("$e")
$ e="aaa\tbbb\tccc\tddd\n" && myar+=("$e")
$ echo -e "${myar[#]}"
a b c d
aa bb cc dd
aaa bbb ccc ddd
#Alternative array printing
$ for i in "${myar[#]}";do echo -en "$i";done
a b c d
aa bb cc dd
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
I have a file:
AA BB CC DD
BB CC DD AA
BB CC DDA AA
CC DD AA BB
This command prints the line:
$ awk '{if($3=="DD") print}' file
BB CC DD AA
I want this condition to write to the array. This command does not work:
$ awk '{if($3=="DD") split($0, a, RS); print a[1]}' file
BB CC DD AA
BB CC DD AA
BB CC DD AA
Thank you for your help.
EDIT:
I wanted to write to an array of lines from the pattern 'DD'.
These are good solutions:
awk '{if($3=="DD") {split($0, a, RS); print a[1];}}' file
awk '$3=="DD"{split($0, a, RS); print a[1];}' file
Thank you for your help.
You're printing the result regardless of whether $3 == "DD", which seems unlikely to be what you want.
You're also splitting with RS which is not set here so for sample output, compare:
awk '{if($3=="DD") {split($0, a); print a[1];}}' file
which splits with FS instead (hence prints just BB for the above).
I'm not completely clear on what you want to do, but try this:
awk '$3 == "DD" { print $1 }' file
The manual splitting you're doing is unneeded in awk.