Bash scripting: variable is not incrementing correctly - bash

I am writing a bash script to loop through all the directories and rename the directory to the value of the array, but it seems my (i th) value is not incrementing correctly and it also says "(i th) command not found" when I run my bash script.
Here is my code: I replaced the values inside Unix with place holder values.
#!/bin/bash
declare -a Unix=(value1 value2 value3 .... );
i = 0
for d in */; do
echo ${Unix[$i]}
#mv $d ${Unix[$i]}
(($i+1))
done

You are doing two things wrong. Firstly , to answer your problem,
(($i+1))
should be
(($i+=1))
also, you should remove the spaces in the line
i = 0
so it looks like
i=0

Firstly, you might want to quote your directory names in the mv command, or you get into trouble with names containing spaces:
mv "$d" "${Unix[i]}"
As you see, it's not necessary to prepend the i in the index with $, as [] is an "arithmetic context" here and expands variable names.
Secondly, your increment does nothing: you just add 1 to i and throw the result away. You can use the increment operator instead:
(( ++i ))
Again, the $ is not needed.

Related

how to assign each of multiple lines in a file as different variable?

this is probably a very simple question. I looked at other answers but couldn't come up with a solution. I have a 365 line date file. file as below,
01-01-2000
02-01-2000
I need to read this file line by line and assign each day to a separate variable. like this,
d001=01-01-2000
d002=02-01-2000
I tried while read commands but couldn't get them to work.It takes a lot of time to shoot one by one. How can I do it quickly?
Trying to create named variable out of an associative array, is time waste and not supported de-facto. Better use this, using an associative array:
#!/bin/bash
declare -A array
while read -r line; do
printf -v key 'd%03d' $((++c))
array[$key]=$line
done < file
Output
for i in "${!array[#]}"; do echo "key=$i value=${array[$i]}"; done
key=d001 value=01-01-2000
key=d002 value=02-01-2000
Assumptions:
an array is acceptable
array index should start with 1
Sample input:
$ cat sample.dat
01-01-2000
02-01-2000
03-01-2000
04-01-2000
05-01-2000
One bash/mapfile option:
unset d # make sure variable is not currently in use
mapfile -t -O1 d < sample.dat # load each line from file into separate array location
This generates:
$ typeset -p d
declare -a d=([1]="01-01-2000" [2]="02-01-2000" [3]="03-01-2000" [4]="04-01-2000" [5]="05-01-2000")
$ for i in "${!d[#]}"; do echo "d[$i] = ${d[i]}"; done
d[1] = 01-01-2000
d[2] = 02-01-2000
d[3] = 03-01-2000
d[4] = 04-01-2000
d[5] = 05-01-2000
In OP's code, references to $d001 now become ${d[1]}.
A quick one-liner would be:
eval $(awk 'BEGIN{cnt=0}{printf "d%3.3d=\"%s\"\n",cnt,$0; cnt++}' your_file)
eval makes the shell variables known inside your script or shell. Use echo $d000 to show the first one of the newly defined variables. There should be no shell special characters (like * and $) inside your_file. Remove eval $() to see the result of the awk command. The \" quoted %s is to allow spaces in the variable values. If you don't have any spaces in your_file you can remove the \" before and after %s.

Open file with two columns and dynamically create variables

I'm wondering if anyone can help. I've not managed to find much in the way of examples and I'm not sure where to start coding wise either.
I have a file with the following contents...
VarA=/path/to/a
VarB=/path/to/b
VarC=/path/to/c
VarD=description of program
...
The columns are delimited by the '=' and some of the items in the 2nd column may contain gaps as they aren't just paths.
Ideally I'd love to open this in my script once and store the first column as the variable and the second as the value, for example...
echo $VarA
...
/path/to/a
echo $VarB
...
/path/to/a
Is this possible or am I living in a fairy land?
Thanks
You might be able to use the following loop:
while IFS== read -r name value; do
declare "$name=$value"
done < file.txt
Note, though, that a line like foo="3 5" would include the quotes in the value of the variable foo.
A minus sign or a special character isn't allowed in a variable name in Unix.
You may consider using BASH associative array for storing key and value together:
# declare an associative array
declare -A arr
# read file and populate the associative array
while IFS== read -r k v; do
arr["$k"]="$v"
done < file
# check output of our array
declare -p arr
declare -A arr='([VarA]="/path/to/a" [VarC]="/path/to/c" [VarB]="/path/to/b" [VarD]="description of program" )'
What about source my-file? It won't work with spaces though, but will work for what you've shared. This is an example:
reut#reut-home:~$ cat src
test=123
test2=abc/def
reut#reut-home:~$ echo $test $test2
reut#reut-home:~$ source src
reut#reut-home:~$ echo $test $test2
123 abc/def

Is there a way to loop variables from another file into my bash script?

Sorry to be a pain, but I'm not sure how I can loop values from an outside file, into my bash script as variables. I have three variable names in my bash script:
$TAGBEGIN
$TAGEND
$MYCODE
In a separate varSrc.txt file, I have several variables:
# a - Some marker
tagBegin_a='/<!-- Begin A -->/'
tagEnd_a='/<!-- End A -->/'
code_a=' [ some code to replace in between tags ] '
# b - Some marker
tagBegin_b='/<!-- Begin B -->/'
tagEnd_b='/<!-- End B -->/'
code_b=' [ some code to replace in between tags ] '
# c - Some marker
...
I need my bash script to be able to loop through each "# marker"* section and perform a function:
source varSrc.txt
$TAGBEGIN
$TAGEND
$MYCODE
...
sed '
'"$TAGEND"' R '"$MYCODE"'
'"$TAGBEGIN"','"$TAGEND"' d
' -i $TARGETDIR
Note: sed code logic (not quoting mess) courtesy of Glenn J.
I need some kind of looping logic like:
for (var i = 0; i <= markers in varSrc.txt ; i++) {
// set bash vars equal to varSrc values
$TAGBEGIN= $tagBegin_i
$TAGEND= $tagEnd_i
$MYCODE= $code_i
// run the 'sed' replace command
sed '
'"$TAGEND"' R '"$MYCODE"'
'"$TAGBEGIN"','"$TAGEND"' d
' -i $TARGETDIR
}
Is this something that can be feasibly done in a bash script and is this a good approach? Any suggestions, pointers or guidance is very, very appreciated!
*(which I don't think is a real marker I can use)
[Answering the question as amended]
There's no need use use, iterate over, or think about markers at all. Leave them out.
source varSrc.txt
for beginVar in "${!tagBegin_#}"; do # Iterate over defined begin variable names
endVar=tagEnd_${var#tagBegin_} # Generate the name of the end variable
codeVar=code_${var#tagBegin_} # Generate the name of the code variable
begin=${!beginVar} # Look up the contents of the begin variable
end=${!endVar} # Look up the contents of the end variable
code=${!codeVar} # Look up the contents of the code variable
sed -e "$end R $code" -e "$begin,$end d" -i "$file"
done
[Answers original, pre-amended question]
source only works if your input file is valid bash syntax; it isn't. Thus, you'll need to parse it yourself, something like the following:
begin= end= code=
while IFS= read -r; do
case $REPLY in
#*)
# we saw a marker; process all vars seen so far
[[ $begin && $end && $code ]] || continue # do nothing if we have no vars seen
sed -e "$end R $code" -e "$begin,$end d" -i "$file"
;;
'$TAGBEGIN='*) begin=${REPLY#'$TAGBEGIN='} ;;
'$TAGEND='*) end=${REPLY#'$TAGEND='} ;;
'$MYCODE='*) code=${REPLY#'$MYCODE='} ;;
esac
done <varSrc.txt
What you can do is export your variables in your second file an the execute the script within your current environment (with a dot before the script) to get the variable names/markers you can parse the file and search for an $ or #

read multiple values from a property file using bash shell script

Would like to read multiple values from a property file using a shell script
My properties files looks something like below, the reason I added it following way was to make sure, if in future more students joins I just need to add in in the properties file without changing any thing in the shell script.
student.properties
total_student=6
student_name_1="aaaa"
student_name_2="bbbb"
student_name_3="cccc"
student_name_4="dddd"
student_name_5="eeee"
When I run below script I not getting the desired output, for reading the student names from properties file
student.sh
#!/bin/bash
. /student.properties
i=1
while [ $i -lt $total_student ]
do
{
std_Name=$student_name_$i
echo $std_Name
#****** my logic *******
} || {
echo "ERROR..."
}
i=`expr $i + 1`
done
Output is something like this
1
2
3
4
5
I understand the script is not getting anything for $student_name_ hence only $i value is getting printed.
Hence, wanted to know how to read values from the properties file.
You can do variable name interpolation with ${!foo}. If $foo is "bar", then ${!foo} gives you the value of $bar. In your code that means changing
std_Name=$student_name_$i
to
var=student_name_$i
std_Name=${!var}
Alternatively, you could store the names in an array. Then you wouldn't have to do any parsing.
student.properties
student_names=("aaaa" "bbbb" "cccc" "dddd" "eeee")
student.sh
#!/bin/bash
. /student.properties
for student_name in "${student_names[#]}"; do
...
done
You can use indirect expansion:
std_Name=student_name_$i
echo "${!std_Name}"
the expression ${!var} basically evaluates the variable twice:
first evaluation: student_name_1
second evaluation: foo
Note that this is rarely a good idea and that using an array is almost always preferred.

Loop two variables through one command in shell

I want to run a shell script that can simultaneously loop through two variables.
So that I can have an input and output file name. I feel like this isn't too hard of a concept but any help is appreciated.
Files = "File1,
File2,
...
FileN
"
Output = OutFile1,
Outfile2,
...
OutfileN
"
and I would in theory my code would be:
for File in $Files
do
COMMAND --file $File --ouput $Output
done
Obviously, there needs to be another loop but I'm stuck, any help is appreciated.
You don't really need to loop 2 variables, just use 2 BASH arrays:
input=("File1" "File2" "File3")
output=("OutFile1" "OutFile2" "OutFile3")
for ((i=0; i<${#input[#]}; i++)); do
echo "Processing input=${input[$i]} and output=${output[$i]}"
done
zsh enables multiple loop variables before the list.
#!/bin/zsh
input2output=(
'File1' 'Outfile1'
'File2' 'Outfile2'
)
for input ouput in $input2output
do
echo "[$input] --> [$ouput]"
done
quotes from zsh(5.9) manual or man zshmisc
for name ... [ in word ... ] term do list done
More than one parameter name can appear before the list of words. If N names are given, then on each execution of the loop the next N words are assigned to the corresponding parameters. If there are more names than remaining words, the remaining parameters are each set to the empty string.

Resources