Shell script - Search through all files? - file

I am trying to write a shell script that will search for a file name (given as an argument) and if the file was modified less than or equal to 10 minutes ago, exit 0, if it was modified more than 10 minutes ago exit 1, and if the file doesn't exist exit 2.
Here's my code:
if find $1
then
if find $1 -mmin -11
then
echo "Exit 0"
else
echo "Exit 1"
fi
else
echo "Exit 2"
fi
How do I make this search through ALL files on the system?
Also, if the file exists then check if it was modified within the past 10 minutes, if it was then exit 1. If the file doesn't exist then exit 2. I have used echo "" so that I can see which exit happens.

if [[ -n $1 ]]; then
find / -name $1 -mmtime -10 2>/dev/null
if [[ $? == 0 ]]; then
exit 0
else
exit 1
fi
else
...

What do you want to do if there are two files of that name found on the filesystem? (For the moment, I'll assume that is not an issue and only work with the first file found.) Your question contradicts itself: in the preamble you say you want to exit with 0 if it has been modified in the last 10 minutes, but later you say that a recent modification should trigger an exit of 1. The following solution returns 0 of the file has NOT been modified in the last 10 minutes. This requires the gnu extension to date that gives the %s format specifier.
#!/bin/sh
fullpath=$( find / -name ${1?No file specified} | sed 1q | grep . ) || exit 2
test $( expr $( date +%s ) - $( stat -c %Y $fullpath )) -gt 600

Related

Unix command to add 2 numbers from 2 separate files and write it to a 3rd file

I have 2 files. I need to add the count of rows of the both and write it to 3rd file.
If the content of 3rd file is >25 , i need to print error.txt and if =<25 , i need to print success.txt
Scenario:
file 1(p.csv) , count: 20
file 2 (m.csv), count : 5
file 3 , count 25
--->should print error.txt
I have tried the below code, but file 3 is not getting generated with expected output.
file_1=$(cat p.csv | wc -l)
echo $file_1
file_2=$(cat m.csv | wc -l)
echo $file_2
file_3 = $(`expr $(file_1) + $(file_2)`)
echo $file_3 > file_3.txt
if [ $file_3 -gt 25 ]; then
touch error.txt
else
touch success.txt
fi
Error message:
20
5
count_test.sh: line 16: file_1: command not found
count_test.sh: line 16: file_2: command not found
expr: syntax error
count_test.sh: line 16: file_3: command not found
count_test.sh: line 20: [: -gt: unary operator expected
Some fixes are required, here is my version:
#!/bin/bash
file_1=$(wc -l p.csv | cut -d' ' -f1)
echo "file_1=$file_1"
file_2=$(wc -l m.csv | cut -d' ' -f1)
echo "file_2=$file_2"
file_3=$(( file_1 + file_2 ))
echo "file_3=$file_3"
if [[ $file_3 -gt 25 ]]
then
echo "ERROR"
touch error.txt
else
echo "Success"
touch success.txt
fi
The arithmetic line was modified to use the $(( )) syntax.
you do not need file_3.txt for the if. If you required it for some other reason, you can put bach the echo "$file_3" > file_3.txt" line.
I added a couple echo statements for debugging purposes.
Some errors you made:
echo $file_1
# Not really wrong, but use quotes
echo "$file_1"
Don't use spaces around = in assignment, don't use backtics and use {} around variables
file_3 = $(`expr $(file_1) + $(file_2)`)
# change this to
file_3=$(expr ${file_1} + ${file_2})
# and consider using (( a = b + c )) for calculations
When you only want the final results, consider
if (( $(cat [pm].csv | wc -l) > 25 )); then
touch error.txt
else
touch success.txt
fi

Bash: rm with an array of filenames

So I'm working on making an advanced delete script. The idea is the user inputs a grep regex for what needs to be deleted, and the script does an rm operation for all of it. Basically eliminates the need to write all the code directly in the command line each time.
Here is my script so far:
#!/bin/bash
# Script to delete files passed to it
if [ $# -ne 1 ]; then
echo "Error! Script needs to be run with a single argument that is the regex for the files to delete"
exit 1
fi
IFS=$'\n'
files=$(ls -a | grep $1 | awk '{print "\"" $0 "\"" }')
## TODO ensure directory support
echo "This script will delete the following files:"
for f in $files; do
echo " $f"
done
valid=false
while ! $valid ; do
read -p "Do you want to proceed? (y/n): "
case $REPLY in
y)
valid=true
echo "Deleting, please wait"
echo $files
rm ${files}
;;
n)
valid=true
;;
*)
echo "Invalid input, please try again"
;;
esac
done
exit 0
My problem is when I actually do the "rm" operation. I keep getting errors saying No such file or directory.
This is the directory I'm working with:
drwxr-xr-x 6 user staff 204 May 9 11:39 .
drwx------+ 51 user staff 1734 May 9 09:38 ..
-rw-r--r-- 1 user staff 10 May 9 11:39 temp two.txt
-rw-r--r-- 1 user staff 6 May 9 11:38 temp1.txt
-rw-r--r-- 1 user staff 6 May 9 11:38 temp2.txt
-rw-r--r-- 1 user staff 10 May 9 11:38 temp3.txt
I'm calling the script like this: easydelete.sh '^tem'
Here is the output:
This script will delete the following files:
"temp two.txt"
"temp1.txt"
"temp2.txt"
"temp3.txt"
Do you want to proceed? (y/n): y
Deleting, please wait
"temp two.txt" "temp1.txt" "temp2.txt" "temp3.txt"
rm: "temp two.txt": No such file or directory
rm: "temp1.txt": No such file or directory
rm: "temp2.txt": No such file or directory
rm: "temp3.txt": No such file or directory
If I try and directly delete one of these files, it works fine. If I even pass that whole string that prints out before I call "rm", it works fine. But when I do it with the array, it fails.
I know I'm handling the array wrong, just not sure exactly what I'm doing wrong. Any help would be appreciated. Thanks.
Consider instead:
# put all filenames containing $1 as literal text in an array
#files=( *"$1"* )
# ...or, use a grep with GNU extensions to filter contents into an array:
# this passes filenames around with NUL delimiters for safety
#files=( )
#while IFS= read -r -d '' f; do
# files+=( "$f" )
#done < <(printf '%s\0' * | egrep --null --null-data -e "$1")
# ...or, evaluate all files against $1, as regex, and add them to the array if they match:
files=( )
for f in *; do
[[ $f =~ $1 ]] && files+=( "$f" )
done
# check that the first entry in that array actually exists
[[ -e $files || -L $files ]] || {
echo "No files containing $1 found; exiting" >&2
exit 1
}
# warn the user
echo "This script will delete the following files:" >&2
printf ' %q\n' "${files[#]}" >&2
# prompt the user
valid=0
while (( ! valid )); do
read -p "Do you want to proceed? (y/n): "
case $REPLY in
y) valid=1; echo "Deleting; please wait" >&2; rm -f "${files[#]}" ;;
n) valid=1 ;;
esac
done
I'll go into the details below:
files has to be explicitly created as an array to actually be an array -- otherwise, it's just a string with a bunch of files in it.
This is an array:
files=( "first file" "second file" )
This is not an array (and, in fact, could be a single filename):
files='"first file" "second file"'
A proper bash array is expanded with "${arrayname[#]}" to get all contents, or "$arrayname" to get only the first entry.
[[ -e $files || -L $files ]]
...thus checks the existence (whether as a file or a symlink) of the first entry in the array -- which is sufficient to tell if the glob expression did in fact expand, or if it matched nothing.
A boolean is better represented with numeric values than a string containing true or false: Running if $valid has potential to perform arbitrary activity if the contents of valid could ever be set to a user-controlled value, whereas if (( valid )) -- checking whether $valid is a positive numeric value (true) or otherwise (false) -- has far less room for side effects in presence of bugs elsewhere.
There's no need to loop over array entries to print them in a list: printf "$format_string" "${array[#]}" will expand the format string additional times whenever it has more arguments (from the array expansion) than its format string requires. Moreover, using %q in your format string will quote nonprintable values, whitespace, newlines, &c. in a format that's consumable by both human readers and the shell -- whereas otherwise a file created with touch $'evil\n - hiding' will appear to be two list entries, whereas in fact it is only one.

How to output 'passed' if a diff command results in no difference in bash?

I am writing a shell script that loops over my ./tests directory and uses the unix diff command to compare .in and .out files against each other for my C program. Here is my shell script:
#! /usr/bin/env bash
count=0
# Loop through test files
for t in tests/*.in; do
echo '================================================================'
echo ' Test' $count
echo '================================================================'
echo 'Testing' $t '...'
# Output results to (test).res
(./snapshot < $t) > "${t%.*}.res"
# Test with diff against the (test).out files
diff "${t%.*}.res" "${t%.*}.out"
echo '================================================================'
echo ' Memcheck
echo '================================================================'
# Output results to (test).res
(valgrind ./snapshot < $t) > "${t%.*}.res"
count=$((count+1))
done
My question is how can I add an if statement to the script that will output 'passed' if the diff command results in no difference? e.g.
pseudo code:
if ((diff res_file out_file) == '') {
echo 'Passed'
} else {
printf "Failed\n\n"
diff res_file out_file
}
Get and check the exit code from the diff command. diff has an exit code of 0 if no differences were found.
diff ...
ret=$?
if [[ $ret -eq 0 ]]; then
echo "passed."
else
echo "failed."
fi
The answer by #jstills worked for me however I modified it slightly and thought I'd post my result as an answer too to help others
Once I understood that diff has an exit code of 0 I modified my code to this. It checks if diff exits with 0 or >1 for each difference if I understand correctly. My code then sends the output of diff to /dev/null so it won't be displayed to stdout and I then do my check and print passed or failed to the stdout and if failed the differences with sdiff which display differences side by side.
if diff "${t%.*}.res" "${t%.*}.out" >/dev/null; then
printf "Passed\n"
else
printf "Failed\n"
sdiff "${t%.*}.res" "${t%.*}.out"
fi

Multidimensional array in bash via associative array

I'm trying to load files from the directory to the associative array with the access like "FDN,4" where FND is the basename of the file and 4 - is the line number:
loadFiles() {
local iter
local comname
local lines
echo "# Loading files"
find ./sys -type f | while read iter
do
comname=$(basename "$iter" .aic)
echo "# $comname"
local i
i=0
while IFS= read -r line
do
commands["$comname,$i"]="$line"
#echo "$comname,$i = ${commands[$comname,$i]}"
((i++))
done < "$iter"
[[ -n $line ]] && commands["$comname,$i"]="$line"
done
}
loadFiles
echo "POP,4 = ${commands[POP,4]}"
I'm getting nothing, the ./sys/dir/POP.aic file exists and the 4th line in this file too. Commented echo inside the cycle shows that value assigns.
Can anyone, please, help and show me where I'm wrong?
Found the root of evil - the subshell. echo "1 2 3" | while <...> will submit the nex subshell, so the variables will be set only locally. The soultion is to use while <...> done < <(find ./sys -type f)

Trouble adding an integer to an array element in shell script

I am trying to parse the output on svn info without resorting to an external shell command like sed or awk. This is purely academic as I know I could do this in a heartbeat with those tools.
Output I am parsing is:
Path: .
URL: svn://brantwinter#192.168.10.222/handbrake_batch/trunk/handbrake
Repository Root: svn://ilium007#192.168.10.222/handbrake_batch
Repository UUID: 99c2cca7-102b-e211-ab20-02060a000c0b
Revision: 6
Node Kind: directory
Schedule: normal
Last Changed Author: ilium007
Last Changed Rev: 6
Last Changed Date: 2012-11-10 19:00:35 +1000 (Sat, 10 Nov 2012)
Here is my code:
#!/bin/bash
#set -x
OLD_IFS="$IFS"
IFS=$'\r\n'
# Get the output from svn info into an array
SVN_INFO_ARR=(`svn info`)
COUNT=0
for i in ${SVN_INFO_ARR[#]}; do
echo $COUNT
echo "$i"
(( COUNT++ ))
done
# Get the element that says "Revision: 6"
REV_ARR=${SVN_INFO_ARR[4]}
# Testing the loop over what should be a two element array
COUNT=0
for i in ${REV_ARR[#]}; do
echo $COUNT
echo "$i"
(( COUNT++ ))
done
#This should give the number 6 (or string or something)
REV_NUMBER=${REV_ARR[1]}
echo ${REV_NUMBER}
### INCREMENT REVISION NUMBER FROM ARRAY ELEMENT ###
#NEW_REV_NUMBER= ????? + 1
IFS="$OLD_IFS"
I would like to be able to take the string:
Revision: 6
and pull out the 6 and increment by 1 so I can update a release txt file to be included in the SVN commit.
I have tried to make that 6 turn into a 7 for an hour now and feel like an idiot because I can't do it.
You need parenthesis: Change this:
# Get the element that says "Revision: 6"
REV_ARR=${SVN_INFO_ARR[4]}
to this:
# Get the element that says "Revision: 6"
REV_ARR=(${SVN_INFO_ARR[4]})
before
#This should give the number 6 (or string or something)
REV_NUMBER=${REV_ARR[1]}
so you'll be able to:
((REV_NUMBER++))
Edit:
As you wrote:
SVN_INFO_ARR=(`svn info`)
instead of just:
SVN_INFO_ARR=`svn info`
The parenthesis is used in bash to define an array. Have a look at:
man -Len -P'less +"/^ *Arrays"' bash
Instead of hardcoding the array indices, a better way would be to filter out the line you need and extract the number
Here's one way using regex (Bash 4)
while read -r line; do
if [[ $line =~ Revision:\ ([0-9]+) ]]; then
new_rev_num=$((BASH_REMATCH[1]+1))
echo $new_rev_num
break
fi
done < $(svn info)
Use grep to only select the line you need. Then, use Parameter expansion to remove "Revision: ". Finally, use let to do the arithmetics:
REVISION=$(svn info | grep '^Revision:')
REVISION=${REVISION#* }
let REVISION++
This code worked in the end:
#!/bin/bash
set -x
OLD_IFS="$IFS"
IFS=$'\r\n'
# Get the output from svn info into an array
SVN_INFO_ARR=(`svn info`)
IFS="$OLD_IFS"
# Get the element that says "Revision: 6"
REV_ARR=(${SVN_INFO_ARR[4]})
#This should give the number 6 (or string or something)
REV_NUMBER=${REV_ARR[1]}
echo $REV_NUMBER
echo $(( REV_NUMBER + 1 ))
The answer above had me stumped for a while because it was missing the $ in front of:
echo $(( REV_NUMBER + 1 ))
and the ((REV_NUMBER++)) notation did not work, I still got 6, not 7:
+ OLD_IFS='
'
+ IFS='
'
+ SVN_INFO_ARR=(`svn info`)
++ svn info
+ IFS='
'
+ REV_ARR=(${SVN_INFO_ARR[4]})
+ REV_NUMBER=6
+ echo 6
6
+ echo 6
6

Resources