bash: concatenating first value of multiple variables - arrays

lets say i used grep and cut to store data into variables. i need to get the first second and third values of each variable concatenated with each other. i think i need to use arrays to achieve this but i don't know how to go about doing that. for example if $one holds a b c and $two holds 1 2 3 and $three holds x y z i want to concatenate so that my output would look like a1x b2y c3z. like i said i think i need to store my grep/cut output into an array but i am not sure how to do that. Thanks.

In pure bash, you can do something like this:
v1="a b c"
v2="1 2 3"
v3="x y z"
for v in v1 v2 v3; do
read p1 p2 p3 <<< ${!v}
a1="$a1$p1"
a2="$a2$p2"
a3="$a3$p3"
done
echo $a1
echo $a2
echo $a3
The last three echoes output:
a1x
b2y
c3z

This might work for you:
v1="a b c"
v2="1 2 3"
v3="x y z"
parallel --xapply echo {1}{2}{3} ::: $v1 ::: $v2 ::: $v3
a1x
b2y
c3z

You can use sed,tr,etc to translate ' ' to '\n'.
Then use paste to concatenate them vertically.
$ v1="a b c"
$ v2="1 2 3"
$ v3="x y z"
$ paste <(tr ' ' '\n' <<<$v1) <(tr ' ' '\n' <<<$v2) <(tr ' ' '\n' <<<$v3) | tr -d '\t'
a1x
b2y
c3z
Or
$ paste <(echo "${v1// /$'\n'}") <(echo "${v2// /$'\n'}") <(echo "${v3// /$'\n'}") | tr -d '\t'
a1x
b2y
c3z
Note: If you save them in separated files, It will be much easier.

Another solution in pure bash using an array :
$ arr=( $v1 $v2 $v3 )
$ for ((i=0; i<3; i++)); do
for ((j=i; j<${#arr[#]}; j+=3)); do printf '%s' ${arr[j]}; done
echo
done
a1x
b2y
c3z

Pure bash using an array:
declare -a a=( $v1 $v2 $v3 )
echo "${a[0]}${a[3]}${a[6]}"
echo "${a[1]}${a[4]}${a[7]}"
echo "${a[2]}${a[5]}${a[8]}"

Related

How to split input string into multiple variable

I'm working on shell script and trying to split user input into multiple variable and use them at different places.
User input is not fixed so can't really assign fixed number of variable, input is separated by comma ,
./user_input.ksh -string /m01,/m02,/m03
#!/bin/ksh
STR=$2
function showMounts {
echo "$STR"
arr=($(tr ',' ' ' <<< "$STR"))
printf "%s\n" "$(arr[#]}"
for x in "$(arr[#]}"
do
free_space=`df -h "$x" | grep -v "Avail" | awk '{print $4}'`
echo "$x": free_space "$free_space"
done
#total_free_space = <total of $free_space>
#echo "$total_free_space"
}
Basically $STR* variable value is filesystem mount points
Host output if run separate df -h command
$ df -h /m01 | grep -v "Avail" | awk '{print $4}'
***Output***
150
Current problems:
(working)1. How to get free space available for each /m* using df -h?
Easiest thing to do is to use shell array here like this:
#!/bin/ksh
str='/m01,/m02,/m03'
arr=($(tr ',' ' ' <<< "$str"))
printf "%s\n" "${arr[#]}"
Output:
/m01
/m02
/m03
To read elements individually you can use:
"${arr[0]}"
"${arr[1]}"
...
Update: Here is your corrected script:
#!/bin/ksh
STR="$2"
arr=($(tr ',' ' ' <<< "$STR"))
printf "<%s>\n" "${arr[#]}"
for x in "${arr[#]}"; do
echo "$x"
free_space=`df -h "$x" | awk '!/Avail/{print $4}'`
echo "$free_space"
done
you can try,
#!/bin/ksh
STR=/m01,/m02,/m03
read STR1 STR2 STR3 <<<`echo $STR | awk 'BEGIN{FS=","; OFS=" "}{$1=$1; print}'`
echo $STR1 - $STR2 - $STR3
you get:
/m01 - /m02 - /m03
A variation on the theme:
# cat user_input.ksh
#!/bin/ksh
c=1
for i in $(echo ${#} | tr "," " ")
do
eval STR$c="$i"
((c=c+1))
done
printf "\$STR1 = %s; \$STR2 = %s; \$STR3 = %s; ...\n" "$STR1" "$STR2" "$STR3"
Which gives you:
# ksh ./user_input.ksh /m01,/m02,/m03,/m04
$STR1 = /m01; $STR2 = /m02; $STR3 = /m03; ...
Hope that helps..
--ab1
$ cat tst.sh
str='/m01,/m02,/m03'
IFS=,
set -- $str
for i
do
echo "$i"
done
$ ./tst.sh
/m01
/m02
/m03
Don't use all-upper-case for variable names unless you are going to export them (by convention and to avoid clashes with built in names like HOME, PATH, IFS, etc.).
For your overall script, you should simply be doing something like this:
df -h "${str//,/ }" | awk '/^ /{print $5, $3; sum+=$3} END{print sum}'
depending on what your df -h output looks like and what you're final output is supposed to be.

Bash pairwise array expansion

I have two arrays of same lengths like following:
arr1[1]=2
arr1[2]=5
arr2[1]=x
arr2[2]=y
I am trying to create a string like "2 x 5 y".
Since the length of the arrays can be a variable, is there any way to do this without using a loop and string concatenation (like parameter expansion or something) ?
You can use paste with process substitution:
arr1[1]=2
arr1[2]=5
arr2[1]=x
arr2[2]=y
s=$(paste <(printf "%s\n" "${arr1[#]}") <(printf "%s\n" "${arr2[#]}") |
tr '[[:space:]]' ' ')
echo "$s"
2 x 5 y

Split string into array Shellscript

How can I split a string into array in shell script?
I tried with IFS='delimiter' and it works with loops (for, while) but I need an array from that string.
How can I make an array from a string?
Thanks!
str=a:b:c:d:e
set -f
IFS=:
ary=($str)
for key in "${!ary[#]}"; do echo "$key ${ary[$key]}"; done
outputs
0 a
1 b
2 c
3 d
4 e
Another (bash) technique:
str=a:b:c:d:e
IFS=: read -ra ary <<<"$str"
This limits the change to the IFS variable only for the duration of the read command.
#!/bin/bash
str=a:b:c:d:e
arr=(${str//:/ })
OUTPUT:
echo ${arr[#]}
a b c d e
Found a solution that doesn't require changing the IFS or a loop:
str=a:b:c:d:e
arr=(`echo $str | cut -d ":" --output-delimiter=" " -f 1-`)
output:
echo ${arr[#]}
a b c d e
Combining the answers above into something that worked for me
set -- `echo $PATH|cut -d':' --output-delimiter=" " -f 1-`; for i in "$#"; do echo $i; done
gives
# set -- `echo $PATH|cut -d':' --output-delimiter=" " -f 1-`; for i in "$#"; do echo $i; done
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
#

How to convert command output to an array line by line in bash?

I'm trying to convert the output of a command like echo -e "a b\nc\nd e" to an array.
X=( $(echo -e "a b\nc\nd e") )
Splits the input for every new line and whitespace character:
$ echo ${#X[#]}
> 5
for i in ${X[#]} ; do echo $i ; done
a
b
c
d
e
The result should be:
for i in ${X[#]} ; do echo $i ; done
a b
c
d e
You need to change your Internal Field Separator variable (IFS) to a newline first.
$ IFS=$'\n'; arr=( $(echo -e "a b\nc\nd e") ); for i in ${arr[#]} ; do echo $i ; done
a b
c
d e
readarray -t ARRAY < <(COMMAND)
Set the IFS to newline. By default, it is space.
[jaypal:~] while IFS=$'\n' read -a arry; do
echo ${arry[0]};
done < <(echo -e "a b\nc\nd e")
a b
c
d e

How can I use a Bash array as input to a command?

Say, for example, I have the following array:
files=( "foo" "bar" "baz fizzle" )
I want to pipe the contents of this array through a command, say sort, as though each element where a line in a file. Sure, I could write the array to a temporary file, then use the temporary file as input to sort, but I'd like to avoid using a temporary file if possible.
If "bar fizzle" didn't have that space character, I could do something like this:
echo ${files[#]} | tr ' ' '\012' | sort
Any ideas? Thanks!
sort <(for f in "${files[#]}" ; do echo "$f" ; done)
Yet another solution:
printf "%s\n" "${files[#]}" | sort
SAVE_IFS=$IFS
IFS=$'\n'
echo "${files[*]}" | sort
IFS=$SAVE_IFS
Of course it won't work properly if there are any newlines in array values.
For sorting files I would recommend sorting in zero-terminated mode (to avoid errors in case of embedded newlines in file names or paths):
files=(
$'fileNameWithEmbeddedNewline\n.txt'
$'saneFileName.txt'
)
echo ${#files[#]}
sort <(for f in "${files[#]}" ; do printf '%s\n' "$((i+=1)): $f" ; done)
sort -z <(for f in "${files[#]}" ; do printf '%s\000' "$((i+=1)): $f" ; done) | tr '\0' '\n'
printf "%s\000" "${files[#]}" | sort -z | tr '\0' '\n'
find . -type f -print0 | sort -z | tr '\0' '\n'
sort -z reads & writes zero-terminated lines!

Resources