How I prepend a string on each item of an array? - bash

I have the following bash script:
#!/bin/bash
items=('mysql_apache','postgresql_apache','maria_apache')
string=""
for i in "${array[#]}"; do
string=$string" -t"$i
done
echo $string
But if I output the string I won't get the expected result:
-t 'mysql_apache' -t 'postgresql_apache' -t 'maria_apache'
DO you have any Idea how I can do this?
Edit 1
I tried the following:
#!/bin/bash
items=('mysql_apache' 'postgresql_apache' 'maria_apache')
string=""
for i in "${array[#]}"; do
string=$string" -t"$i
done
echo $string
But I still do not get the expected output.

Array elements are separated by whitespace, not commas. Also, items != array.
#! /bin/bash
items=(mysql_apache postgresql_apache maria_apache)
string=""
for i in "${items[#]}"; do
string+=" -t $i"
done
echo $string
But you don't need a loop at all:
items=(mysql_apache postgresql_apache maria_apache)
echo ${items[#]/#/-t }
The substitution can be applied to every element of an array. The /# matches at the start of each string.

You're close. Your forgot to change ${array[#]} in the for loop to what your array was named: items or specifically ${items[#]} You also needed a few other little changes, see below:
#!/bin/bash
declare -a items=('mysql_apache' 'postgresql_apache' 'maria_apache')
string=""
for i in "${items[#]}"; do
string=${string}" -t "$i
done
echo $string
Lastly if you want to see what is happening you can add temporary echo statements to see what if anything is changing:
for i in "${items[#]}"; do
string=${string}" -t "$i
echo >>>$string<<<
done

Related

Bash shellscript array elements are not ordered

I had this code, when I stored hostname to CustomHostName array, I expected it should store in order, it seems not stored correctly when i tried to print out or use that array
declare -A CustHostName
for i in "${UdmPodsList[#]}" ; do
hostname=`kubectl get pods -n $NameSpace -o wide|grep $i |awk '{print $7}'`
CustHostName["$i"]="$hostname"
echo "$hostname"
done
echo "${CustHostName[#]}"
echo $hostname, will print out as in this order
vudmvzcl00-worker-02
vudmvzcl00-worker-03
echo "${CustHostName[#]}", or used it in for loop, it had worker-03 as first element
vudmvzcl00-worker-03 vudmvzcl00-worker-02
I am trying to sort the array but this is not what I wanted to do.
I want the vudmvzcl00-worker-02 to be first element to on the array list when I used.
Thanks.
replace
echo "${CustHostName[#]}"
with
for key in $(sort <<<"${!CustHostName[#]}"); do
echo "${CustHostName[$key]}"
done
explanation
${!CustHostName[#]}" --> get the keys, not the values
I tried this way, it seems working for me.
CustomHostName=()
n=0
for i in "${UdmPodsList[#]}" ; do
((n++))
hostname=`kubectl get pods -n $NameSpace -o wide|grep $i |awk '{print $7}'`
CustHostName[$n]="$hostname"
echo "$hostname"
done
output as I expected. no sorting needed.
echo "${CustHostName[#]}"
vudmvzcl00-worker-02 vudmvzcl00-worker-03
Or print in the array.
for HOST in "${!CustHostName[#]}" ; do
echo "${CustHostName[$HOST]}"
done
output(ArrayPrint):
vudmvzcl00-worker-02
vudmvzcl00-worker-03
Thanks.

Find and replace few words in text file with using bash

I have a script to, where in one variable words, that i have in file, in other variable, i have words, that i want use instead words from first variable. I need to find i am scatman and replace these words to you are dukenukem. For example, my text file, wwe.txt:
i
am
dsadsa
sda
daaaa
ds
dsds
dsa
d
scatman
For example, i wrote script, that makes grep, and it works:
words="i am scatman"
echo "$words"
for i in $words; do
if grep -q "$i" wwe.txt; then
echo "these words are exists"
grep "$i" wwe.txt
else
echo "these words are not exists"
exit 1
fi
done
It works. But if i want, to replace these words, how i can do this ? i wrote this:
words="i am scatman"
words2="you are dukenukem"
for i in $words; do
for y in $words2; do
if grep -q "$i" wwe.txt; then
echo "these words are exists"
grep "$i" wwe.txt
sed -i 's/'"$i"'/'"$y"'/g' wwe.txt
else
echo "these words are not exists"
exit 1
fi
done
done
But it does not work, where i have error ? Help please.
This code works. Please try it out.
#!/bin/bash
line1="i am scatman"
line2="you are dukenukem"
words2=($line2)
count=0
for word in $line1; do
sed -i -e "s/$word/${words2[$count]}/g" wwe.txt
count=$((count + 1))
done

Store array output to comma separated list in bash scripting

I have taken input from user into array. But I need to use them as comma separated list. How can I do that? Input in my case is path like (/usr/tmp/). I appreciate your help and time. Thank you !
Example:
read "Number of subdirectories : " count
for i in $(seq 1 $count)
do
read -e -p " Subdir : $i: " arr[$i]
done
Expected Result:
$var = {arr[1],arr[2],arr[3],......}
If you have an array like this:
$ declare -p arr
declare -a arr='([1]="abc" [2]="def")'
You can display it in comma-separated format:
$ (IFS=,; echo "{${arr[*]}}")
{abc,def}
That output can be saved in a shell variable using command substitution:
$ var=$(IFS=,; echo "{${arr[*]}}")
$ echo "$var"
{abc,def}

parse and expand interval

In my script I need to expand an interval, e.g.:
input: 1,5-7
to get something like the following:
output: 1,5,6,7
I've found other solutions here, but they involve python and I can't use it in my script.
Solution with Just Bash 4 Builtins
You can use Bash range expansions. For example, assuming you've already parsed your input you can perform a series of successive operations to transform your range into a comma-separated series. For example:
value1=1
value2='5-7'
value2=${value2/-/..}
value2=`eval echo {$value2}`
echo "input: $value1,${value2// /,}"
All the usual caveats about the dangers of eval apply, and you'd definitely be better off solving this problem in Perl, Ruby, Python, or AWK. If you can't or won't, then you should at least consider including some pipeline tools like tr or sed in your conversions to avoid the need for eval.
Try something like this:
#!/bin/bash
for f in ${1//,/ }; do
if [[ $f =~ - ]]; then
a+=( $(seq ${f%-*} 1 ${f#*-}) )
else
a+=( $f )
fi
done
a=${a[*]}
a=${a// /,}
echo $a
Edit: As #Maxim_united mentioned in the comments, appending might be preferable to re-creating the array over and over again.
This should work with multiple ranges too.
#! /bin/bash
input="1,5-7,13-18,22"
result_str=""
for num in $(tr ',' ' ' <<< "$input"); do
if [[ "$num" == *-* ]]; then
res=$(seq -s ',' $(sed -n 's#\([0-9]\+\)-\([0-9]\+\).*#\1 \2#p' <<< "$num"))
else
res="$num"
fi
result_str="$result_str,$res"
done
echo ${result_str:1}
Will produce the following output:
1,5,6,7,13,14,15,16,17,18,22
expand_commas()
{
local arg
local st en i
set -- ${1//,/ }
for arg
do
case $arg in
[0-9]*-[0-9]*)
st=${arg%-*}
en=${arg#*-}
for ((i = st; i <= en; i++))
do
echo $i
done
;;
*)
echo $arg
;;
esac
done
}
Usage:
result=$(expand_commas arg)
eg:
result=$(expand_commas 1,5-7,9-12,3)
echo $result
You'll have to turn the separated words back into commas, of course.
It's a bit fragile with bad inputs but it's entirely in bash.
Here's my stab at it:
input=1,5-7,10,17-20
IFS=, read -a chunks <<< "$input"
output=()
for chunk in "${chunks[#]}"
do
IFS=- read -a args <<< "$chunk"
if (( ${#args[#]} == 1 )) # single number
then
output+=(${args[*]})
else # range
output+=($(seq "${args[#]}"))
fi
done
joined=$(sed -e 's/ /,/g' <<< "${output[*]}")
echo $joined
Basically split on commas, then interpret each piece. Then join back together with commas at the end.
A generic bash solution using the sequence expression `{x..y}'
#!/bin/bash
function doIt() {
local inp="${#/,/ }"
declare -a args=( $(echo ${inp/-/..}) )
local item
local sep
for item in "${args[#]}"
do
case ${item} in
*..*) eval "for i in {${item}} ; do echo -n \${sep}\${i}; sep=, ; done";;
*) echo -n ${sep}${item};;
esac
sep=,
done
}
doIt "1,5-7"
Should work with any input following the sample in the question. Also with multiple occurrences of x-y
Use only bash builtins
Using ideas from both #Ansgar Wiechers and #CodeGnome:
input="1,5-7,13-18,22"
for s in ${input//,/ }
do
if [[ $f =~ - ]]
then
a+=( $(eval echo {${s//-/..}}) )
else
a+=( $s )
fi
done
oldIFS=$IFS; IFS=$','; echo "${a[*]}"; IFS=$oldIFS
Works in Bash 3
Considering all the other answers, I came up with this solution, which does not use any sub-shells (but one call to eval for brace expansion) or separate processes:
# range list is assumed to be in $1 (e.g. 1-3,5,9-13)
# convert $1 to an array of ranges ("1-3" "5" "9-13")
IFS=,
local range=($1)
unset IFS
list=() # initialize result list
local r
for r in "${range[#]}"; do
if [[ $r == *-* ]]; then
# if the range is of the form "x-y",
# * convert to a brace expression "{x..y}",
# * using eval, this gets expanded to "x" "x+1" … "y" and
# * append this to the list array
eval list+=( {${r/-/..}} )
else
# otherwise, it is a simple number and can be appended to the array
list+=($r)
fi
done
# test output
echo ${list[#]}

Loading variables from a text file into bash script

Is it possible to load new lines from a text file to variables in bash?
Text file looks like?
EXAMPLEfoo
EXAMPLEbar
EXAMPLE1
EXAMPLE2
EXAMPLE3
EXAMPLE4
Variables become
$1 = EXAMPLEfoo
$2 = EXAMPLEbar
ans so on?
$ s=$(<file)
$ set -- $s
$ echo $1
EXAMPLEfoo
$ echo $2
EXAMPLEbar
$ echo $#
EXAMPLEfoo EXAMPLEbar EXAMPLE1 EXAMPLE2 EXAMPLE3 EXAMPLE4
I would improve the above by getting rid of temporary variable s:
$ set -- $(<file)
And if you have as input a file like this
variable1 = value
variable2 = value
You can use following construct to get named variables.
input=`cat filename|grep -v "^#"|grep "\c"`
set -- $input
while [ $1 ]
do
eval $1=$3
shift 3
done
cat somefile.txt| xargs bash_command.sh
bash_command.sh will receive these lines as arguments
saveIFS="$IFS"
IFS=$'\n'
array=($(<file))
IFS="$saveIFS"
echo ${array[0]} # output: EXAMPLEfoo
echo ${array[1]} # output: EXAMPLEbar
for i in "${array[#]}"; do echo "$i"; done # iterate over the array
Edit:
The loop in your pastebin has a few problems. Here it is as you've posted it:
for i in "${array[#]}"; do echo " "AD"$count = "$i""; $((count=count+1)); done
Here it is as it should be:
for i in "${array[#]}"; do declare AD$count="$i"; ((count=count+1)); done
or
for i in "${array[#]}"; do declare AD$count="$i"; ((count++)); done
But why not use the array directly? You could call it AD instead of array and instead of accessing a variable called "AD4" you'd access an array element "${AD[4]}".
echo "${AD[4]}"
if [[ ${AD[9]} == "EXAMPLE value" ]]; then do_something; fi
This can be done be with an array if you don't require these variables as inputs to a script. push() function lifted from the Advanced Scripting Guide
push() # Push item on stack.
{
if [ -z "$1" ] # Nothing to push?
then
return
fi
let "SP += 1" # Bump stack pointer.
stack[$SP]=$1
return
}
The contents of /tmp/test
[root#x~]# cat /tmp/test
EXAMPLEfoo
EXAMPLEbar
EXAMPLE1
EXAMPLE2
EXAMPLE3
EXAMPLE4
SP=0; for i in `cat /tmp/test`; do push $i ; done
Then
[root#x~]# echo ${stack[3]}
EXAMPLE1
None of the above will work, if your values are quoted with spaces.
However, not everythinf is lost.
Try this:
eval "$(VBoxManage showvminfo "$VMname" --details --machinereadable | egrep "^(name|UUID|CfgFile|VMState)")"
echo "$name {$UUID} $VMState ($VMStateChangeTime) CfgFile=$CfgFile"
P.S.
Nothing will ever work, if your names are quoted or contain dashes.
If you have something like that, as is the case with VBoxManage output ("IDE-1-0"="emptydrive" and so on), either egrep only specific values, as shown in my example, or silence the errors.
However, silencing erors is always dangerous. You never know, when the next value will have unquoted "*" in it, thus you must treat values loaded this way very careful, with all due precaution.

Resources