Increment Number in ZSH script - bash

I am trying to create directories based off of the number of files I have in a directory and my approach is a loop.
I am keeping track of the number by a number variable and want to increment it as so after each even number, create a directory.
This is my code so far:
#!/bin/zsh
LOOP=$(ls 2021/*.xlsx)
totalFiles=$(ls 2021/*.xlsx | wc -l)
directoryCount=0
count=0
for FILE in ${LOOP[#]}; do
echo "BEFORE: $count"
count=$((count+1))
echo "AFTER: $count"
echo $FILE
done
echo "TOTAL FILE: $totalFiles"
echo $count
and the output I get is:
BEFORE: 0
AFTER: 1
2021/*Tanner2103.xlsx
2021/*Tanner2104.xlsx
2021/*Tanner2105.xlsx
2021/*Tanner2106.xlsx
TOTAL FILE: 4
1
I dont understand why count only increments once but the loop is obviously has more iterations than that.
So basically since there are 4 files, I want to split them up into 2 directories, Ill do the logic for that later. But for now Im just trying to get the directory code working.

Ok I feel silly. I thought that ls would come back as an iterable but its actual only return one iteration as a whole.
This is my updated code and it updating like I want.
totalFiles=$(ls 2021/*.xlsx | wc -l)
count=0
for FILE in 2021/**/*(.); do
echo "BEFORE $count"
echo "$FILE"
((count++))
echo "AFTER $count"
done
echo "TOTAL FILE: $totalFiles"
echo $count
result:
BEFORE 0
2021/*Tanner2103.xlsx
AFTER 1
BEFORE 1
2021/*Tanner2104.xlsx
AFTER 2
BEFORE 2
2021/*Tanner2105.xlsx
AFTER 3
BEFORE 3
2021/*Tanner2106.xlsx
AFTER 4
TOTAL FILE: 4
4

Related

Nested while loop not working in Bash

Beginner here so bear with me. I am trying to compare homework submissions from a solution file and a student submission file. The contents of each file have three problems, one per line:
problem 1 code
problem 2 code
problem 3 code
I want to compare each line in the solution with the corresponding line in the students submission. I am using a for loop to run through each student file and a nested while loop to run through each line of the solution file and student file. For some reason the script is completely ignoring the while loop. I have put echoes between each line to see where the problem is(the echo $solution and echo $submission is just to check to see if the path is correct):
for submission in /home/myfolder/submissions/*
do
echo 1
solution=$(echo /home/myfolder/hwsolution/*)
echo 2
echo $solution
echo $submission
while read sans <&1 && read sol <&2
do
echo 3
echo Student awnser is: $sans
echo Solution is: $sol
echo 4
done 1<$(echo $submission) 2<$(echo $(echo $solution))
echo 5
done
When I run it I get:
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student1
5
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student2
5
1
2
/home/myfolder/hwsolution/solution
/home/myfolder/submissions/student3
5
It's not ignoring the while loop -- you're redirecting the file descriptors used for stdout and stderr, so echo can't write to the console within it.
for submission in /home/myfolder/submissions/*; do
solutions=( /home/myfolder/hwsolution/* )
if (( ${#solutions[#]} == 1 )) && [[ -e ${solutions[0]} ]]; then
solution=${solutions[0]}
else
echo "Multiple solution files found; don't know which to use" >&2
printf ' - %q\n' "${solutions[#]}" >&2
exit
fi
while read sans <&3 && read sol <&4; do
echo "Student awnser is: $sans"
echo "Solution is: $sol"
done 3<"$submission" 4<"$solution"
done
The most immediate change is that we're redirecting FD3 and FD4, not FD1 and FD2.

Comparing 2 lists of users in bash

I have 2 variables that contains a list of users
echo $old_users
1
2
3
4
5
6
echo $new_users
1
2
3
4
I want to know which users from the old_users list were removed in the new_users list (here user 5 and 6). This is what I have written so far but I still miss something:
echo $old_users | while read line
do
if echo "$new_users"| grep "$line"
then
echo "$line user was removed"
else
echo "$line user is still there"
fi
done
Any help appreciated! Thanks
for line in $old_users
do
if echo "$new_users"| grep "$line" #[ You can use grep -w for exact match here ]
then
echo "$line user was removed"
else
echo "$line user is still there"
fi
done
Hope it will work now.
This is somewhat similar to https://superuser.com/a/135180. So using that here might be beneficial. This will take the set difference of old_users - new_users.
diff --changed-group-format='%<' --unchanged-group-format='' <(echo $old_users) <(echo $new_users)

For loop structure to add in bash

I'm new to this and what I'm trying to do is create a simple adding script in bash.
I have to use a for loop. What I'm starting so far looks like this:
#!/bin/bash
sum=0
for num in {1..15}
do
echo $num
done
echo$sum
I need help with how to make the for loop show for example if I type:
add 4 -3 2 8
it will output as:
4
-3
2
8
=11
How would I make it so the $num only show what I typed in such as the '4 -3 2 8' and negative numbers?
You can use $# to get all parameters, and $(()) to do arithmetic.
sum=0
for num in $#
do
sum=$((sum + num))
done
echo $# = $sum
I'll retag your question as bash; d is not appropriate.
I'm trying to create an exit error to expand on the previous script now. if I type anything but a number now and what I have tried so far is:
sum=0
for num in "$#"
do
echo $num | grep -i [^0-9+-]
if ["$?" = 1] then
echo "Sorry, '$num' is not a number"
fi
sum=$((sum + num))
done
echo $sum
Example if I type in
add 1 2 3 four five
it would say
four
Sorry, 'four' is not a number

Variable incremented in bash while loop resets to 0 when loop finishes [duplicate]

This question already has answers here:
Local variables after loop exit
(5 answers)
Closed 6 years ago.
I'm writing a bash script that uses a while loop to process over the rows outputted from a specific command. I also increment a variable (adding 1) for each row found.
Heres an example of the section of the script in question:
#!/bin/bash
count=0
ls | while read f
do
count=$(($count+1))
echo "Count is at ${count}"
done
echo "Found total of ${count} rows"
Basically, it increments the $count variable just fine, but then when I print $count after the while loop.. its reset to 0..
Example output:
Count is at 1
Count is at 2
Count is at 3
Count is at 4
Count is at 5
Found total of 0 rows
Any idea why the $count would reset after the loops done?
I also tried adding the last echo statement using the && operator on the loop, like so:
count=0
ls | while read f
do
count=$(($count+1))
echo "Count is at ${count}"
done && echo "Found total of ${count} rows"
With no success.
Any help would be appreciated
A pipe spawns a subshell, use a process substitutions instead:
while read -r f
do
count=$(($count+1))
echo "Count is at ${count}"
done < <(ls)
Also note that you shouldn't parse the output of ls.
And your example seems to count numbers of files and directories in current directory, which can be done with find and wc:
find -maxdepth 1 -mindepth 1 -printf "\n" | wc -l
or you can avoid ls with a for loop and globbing:
for f in * .*; do
[ -e "$f" ] || continue
count=$((count + 1))
echo "Count is at ${count}"
done

issue with if statement in bash

I have issue with an if statement. In WEDI_RC is saved log file in the following format:
name_of_file date number_of_starts
I want to compare first argument $1 with first column and if it is true than increment number of starts. When I start my script it works but just with one file, eg:
file1.c 11:23:07 1
file1.c 11:23:14 2
file1.c 11:23:17 3
file1.c 11:23:22 4
file2.c 11:23:28 1
file2.c 11:23:35 2
file2.c 11:24:10 3
file2.c 11:24:40 4
file2.c 11:24:53 5
file1.c 11:25:13 1
file1.c 11:25:49 2
file2.c 11:26:01 1
file2.c 11:28:12 2
Every time when I change file it begin counts from 1. I need to continue with counting when it ends.
Hope you understand me.
while read -r line
do
echo "line:"
echo $line
if [ "$1"="$($line | grep ^$1)" ]; then
number=$(echo $line | grep $1 | awk -F'[ ]' '{print $3}')
else
echo "error"
fi
done < $WEDI_RC
echo "file"
((number++))
echo $1 `date +"%T"` $number >> $WEDI_RC
There are at least two ways to resolve the problem. The most succinct is probably:
echo "$1 $(date +"%T") $(($(grep -c "^$1 " "$WEDI_RC") + 1))" >> "$WEDI_RC"
However, if you want to have counts for each file separately, you can do that using an associative array, assuming you have Bash version 4.x (not 3.x as is provided on Mac OS X, for example). This code assumes the file is correctly formatted (so that the counts do not reset to 1 each time the file name changes).
declare -A files # Associative array
while read -r file time count # Split line into three variables
do
echo "line: $file $time $count" # One echo - not two
files[$file]="$count" # Record the current maximum for file
done < "$WEDI_RC"
echo "$1 $(date +"%T") $(( ${files[$1]} + 1 ))" >> "$WEDI_RC"
The code uses read to split the line into three separate variables. It echoes what it read and records the current count. When the loop's done, it echoes the data to append to the file. If the file is new (not mentioned in the file yet), then you will get a 1 added.
If you need to deal with the broken file as input, then you can amend the code to count the number of entries for a file, instead of trusting the count value. The bare-array reference notation used in the (( … )) operation is necessary when incrementing the variable; you can't use ${array[sub]}++ with the increment (or decrement) operator because that evaluates to the value of the array element, not its name!
declare -A files # Associative array
while read -r file time count # Split line into three variables
do
echo "line: $file $time $count" # One echo - not two
((files[$file]++)) # Count the occurrences of file
done < "$WEDI_RC"
echo "$1 $(date +"%T") $(( ${files[$1]} + 1 ))" >> "$WEDI_RC"
You can even detect whether the format is in the broken or fixed style:
declare -A files # Associative array
while read -r file time count # Split line into three variables
do
echo "line: $file $time $count" # One echo - not two
if [ $((files[$file]++)) != "$count" ]
then echo "$0: warning - count out of sync: ${files[$file]} vs $count" >&2
fi
done < "$WEDI_RC"
echo "$1 $(date +"%T") $(( ${files[$1]} + 1 ))" >> "$WEDI_RC"
I don't get exactly what you want to achieve with your test [ "$1"="$($line | grep ^$1)" ] but it seems you are checking that the line start with the first argument.
If it is so, I think you can either:
provide the -o option to grep so that it print just the matched output (so $1)
use [[ "$line" =~ ^"$1" ]] as test.

Resources