Bash-scripting. Check file-status - bash

I've been handed an assignment which goes as follows;
I should write a program that checks a file and print out if one of the following happens:
File is created
file is modified
file is deleted
I've made a working program ( my noobish way):
bool=true
if [ -e "$1" ]
then
dato=$(date -r "$1" "+%s")
datony=$(date -r "$1" "+%s")
while [ "$bool" = true ]
do
echo "File exists!"
sleep $2
datony=$(date -r "$1" "+%s")
if [ "$dato" -ne "$datony" ]
then
bool=false
echo "File modified!"
fi
if [ ! -e "$1" ]; then
bool=false
echo "File erased"
fi
done
fi
if [ ! -e "$1" ]
then
while [ "$bool" = true ]
do
echo "File does not exist!"
sleep $2
if [ -e "$1" ]
then
bool=false
echo "File created!"
fi
done
fi
The problem now is that i should make a new script that should take multiple files and check their statuses using my existing script. I need som help with how i should use my script to do that.

If you want to continue using $1 for files and $2 for sleep time.
first, put sleep time in a variable
sleepTime=$2
Put all the code you already made in a function (or a program)
checkFile(){
bool=true
if [ -e "$1" ]
then
dato=$(date -r "$1" "+%s")
datony=$(date -r "$1" "+%s")
while [ "$bool" = true ]
do
echo "File exists!"
sleep $2
datony=$(date -r "$1" "+%s")
if [ "$dato" -ne "$datony" ]
then
bool=false
echo "File modified!"
fi
if [ ! -e "$1" ]; then
bool=false
echo "File erased"
fi
done
fi
if [ ! -e "$1" ]
then
while [ "$bool" = true ]
do
echo "File does not exist!"
sleep $2
if [ -e "$1" ]
then
bool=false
echo "File created!"
fi
done
fi
}
create another function to separate the files and call the other function on background.
func_splitFiles(){
while [ ! -z $1 ];
do
checkFile $1 $sleepTime &
shift;
done;
}
In the "main" program you just call the function to split files passing $1
func_splitFiles $1
You should call the program passing files between quotes.
program.sh "file1 file2 file3" 1

Related

Bash script to save each line of a file to a series of files?

I've got a file that has a json string on each line of the file
I'd like to save each line of that file to a separate json file, and optionally run a command on each line before it's saved to a file.
How can I do this?
The following will do this
save_each_line_of_file_to_a_file.sh /tmp/dump.json
save_each_line_of_file_to_a_file.sh
#!/usr/bin/env bash
function show_help()
{
IT="
usage: FILE {OUTPUT_PREFIX} {OUTPUT_POSTFIX} ${OUTPUT_FOLDER}
e.g.
/tmp/myfile -> generates files in pwd, 1,2,3,4, etc for each line
/tmp/myfile OUT json -> generates files in pwd, OUT1.json,OUT2.json,OUT3.json,OUT4.json, etc for each line
"
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ -z "$1" ]
then
show_help
fi
OUTPUT_PREFIX=${2:-}
OUTPUT_POSTFIX=${3:-}
OUTPUT_FOLDER=${4:-}
EXTRA_CMD=${5:-}
FILE=${1:-/dev/stdin}
INDEX=1
while read line
do
OUTPUT_FILENAME="${OUTPUT_FOLDER}${OUTPUT_PREFIX}$INDEX${OUTPUT_POSTFIX}"
echo $OUTPUT_FILENAME
INDEX=$((INDEX+1))
if [ -z "$EXTRA_CMD" ]
then
echo $line > $OUTPUT_FILENAME
else
echo $line | $EXTRA_CMD > $OUTPUT_FILENAME
fi
done < "$FILE"
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ -z "$1" ]
then
show_help
fi
OUTPUT_PREFIX=${2:-}
OUTPUT_POSTFIX=${3:-}
OUTPUT_FOLDER=${4:-}
EXTRA_CMD=${5:-}
FILE=${1:-/dev/stdin}
INDEX=1
while read line
do
OUTPUT_FILENAME="${OUTPUT_FOLDER}${OUTPUT_PREFIX}$INDEX${OUTPUT_POSTFIX}"
echo $OUTPUT_FILENAME
INDEX=$((INDEX+1))
if [ -z "$EXTRA_CMD" ]
then
echo $line > $OUTPUT_FILENAME
else
echo $line | $EXTRA_CMD > $OUTPUT_FILENAME
fi
done < "$FILE"

UNIX terminal - shell won't terminate, stuck in for loop

I'm having some trouble terminating a shell. I'm starting several filkontroll.sh in the background to regularly check files to see if they have been changed or deleted. It seems to be running fine except that when all the files have been modified, the program won't terminate. It's seems to be stuck in the for-loop somehow.
filkontroll.sh :
#!/bin/bash
clear
declare -i status=1
if [ -f $1 ]
then
status=0
timestamp=$(stat -f "%Sm" -t "%H%M%S" $1)
fi
while [ 0 ]
do
if [ -f $1 ] && [ $status -eq 1 ]
then
echo "Filen $1 ble opprettet."
break
elif [ ! -f $1 ] && [ $status -eq 0 ]
then
echo "Filen $1 ble slettet."
break
elif [ -f $1 ]
then
sistEndret=$(stat -f "%Sm" -t "%H%M%S" $1)
if [ ! $sistEndret -eq $timestamp ]
then
echo "Filen $1 ble endret."
break
fi
fi
sleep $2
done
kontrollflerefiler.sh:
#!/bin/bash
clear
for fil in $#
do
. filkontroll.sh $fil 2 &
done

Check if arguments[files] exist or not in bash

This is part of my script:
#!/bin/bash
USAGE(){
echo "Usage: ./`basename $0` <File1> <File2>"
}
if [ "$#" -ne "2" ]; then
USAGE
exit 1
fi
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
I want to check if file1 does not exist prints:
The file "file1" does not exist!
if file2 does not exist prints:
The file "file2" does not exist!
If both does not exist prints:
The files "file1" and "file2" don't exist!
How can I do that?
I want to know what the most logical (STANDARD) method is.
Of course you can do that... . There are many ways to obtain this. The most simple maybe:
if [ ! -f "$1" ] && [ ! -f "$2" ]; then
echo "The files \"$1\" and \"$2\" do not exist!"
exit 1
else
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
fi
If you don't want to do the checks two time, you can work with variables; something like this:
if [ ! -f "$1" ]; then
NOT1=1
fi
if [ ! -f "$1" ]; then
NOT2=1
fi
if [ -n "$NOT1" ] && [ -n "$NOT2" ]
....
You could do it like this so you only have to test each file once:
status=""
for file; do
[ -f "$file" ]
status+=$?
done
case $status in
11)
echo "The files \"$1\" and \"$2\" do not exist!"
exit 1
;;
10)
echo "The file \"$1\" does not exist!"
exit 1
;;
01)
echo "The file \"$2\" does not exist!"
exit 1
;;
esac
The logical is
if [ ! -f "$1" ]; then
if [ ! -f "$2" ]; then
echo "The files \"$file1\" and \"$file2\" don't exist!"
exit 1
else
echo "The file \"$1\" does not exist!"
exit 1
fi
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
The readable is
if [ ! -f "$1" -a ! -f "$2" ]; then
echo "The files \"$file1\" and \"$file2\" don't exist!"
exit 1
fi
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
The readable is preferred.
Also not testing for existence twice as Chris Maes said could be logical too.
Simplified for Bash:
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Usage: ${0##*/} <File1> <File2>"
elif [[ ! -f $1 && ! -f $2 ]]; then
echo "The files \"$1\" and \"$2\" don't exist!"
elif [[ ! -f $1 ]]; then
echo "The file \"$1\" does not exist!"
elif [[ ! -f $2 ]]; then
echo "The file \"$2\" does not exist!"
fi
You can use this additional condition:
if [ ! -f "$1" ] && [ ! -f "$2" ]; then
echo "Both files does not exist!"
exit 1
fi
I'm not sure you need that though. The two conditions you have, for first and second file, will be informative enough.
Alternatively, you can use just one condition by using logical OR ||. I'd suspect users wouldn't have an issue with understanding their misues of the script:
if [ ! -f "$1" ] || [ ! -f "$2" ]; then
USAGE
exit 1
fi

Bash script: Copies image files from source dir to destination dir and adds an extra suffix on files with same file name

I have this script that copies image files from source directory to destination directory. There are some image files in the source directory that have the same name but different file size. This script also compares the two files with the same name using a stat command. Now, I want to add a string suffix e.g. IMG0897.DUP.JPG before the file extension to the files with the same file name that are going to be copied over to the destination folder. At the moment, my script adds the file size of the file to the file name.
I need help on how to add a string of text of my own rather than the size of the file.
Here's my script:
#!/bin/sh
SEARCH="IMG_*.JPG"
SOURCE= $1
DEST=$2
test $# -ne 2 && echo Usage : phar image_path archive_path
if [ ! -e $1 ]
then echo Source folder does not exist
fi
if [ ! -e $2 ]
then mkdir $2/
fi
# Execute the script.
if [ "${SEARCH%% *}" = "$SEARCH" ]; then
command="find \"$1\" -name \"$SEARCH\""
else
command="find \"$1\" -name \"${SEARCH%% *}\""$(for i in ${SEARCH#* }; do echo -n " -o -name \"$i\""; done)
fi
# Run the main loop.
eval "$command" | while read file; do
bn=$(basename "$file")
bc=$(stat -c%s "$file")
if [ -f "${2}/$bn" ] && [ "$bc" -ne $(stat -c%s "${2}/$bn") ]; then
bn="$bn.$bc"
fi
if [ -f "${2}/$bn" ]; then
echo "File ${2}/$bn already exists."
else
echo "Copying $file to $2/$bn"
cp -a "$file" "$2/$bn"
fi
done
exit 0
else
echo "Error : Can't find $1 or $2"
exit 1
fi
I modified your scripte slightly.
#!/bin/sh
SEARCH="IMG_*.JPG"
SOURCE=$1
DEST=$2
SUFFIX=DUP
test $# -ne 2 && echo Usage : phar image_path archive_path
if [ ! -e $1 ]
then echo Source folder does not exist
fi
if [ ! -e $2 ]
then mkdir $2/
fi
# Execute the script.
if [ "${SEARCH%% *}" = "$SEARCH" ]; then
command="find \"$1\" -name \"$SEARCH\""
else
command="find \"$1\" -name \"${SEARCH%% *}\""$(for i in ${SEARCH#* }; do echo -n " -o -name \"$i\""; done)
fi
# Run the main loop.
eval "$command" | while read file; do
bn=$(basename "$file")
bc=$(stat -c%s "$file")
if [ -f "${2}/$bn" ] && [ "$bc" -ne $(stat -c%s "${2}/$bn") ]; then
bc=$(echo ${bn}|cut -d. -f2)
bn=$(echo ${bn}|cut -d. -f1)
bn=$bn.$SUFFIX.$bc**
fi
if [ -f "${2}/$bn" ]; then
echo "File ${2}/$bn already exists."
else
echo "Copying $file to $2/$bn"
cp -a "$file" "$2/$bn"
fi
done
exit 0
else
echo "Error : Can't find $1 or $2"
exit 1
fi
My execution result is:
root#precise32:/vagrant# sh JPG_moves.sh /root/dir1/ /root/destination/
Copying /root/dir1/IMG_0897.JPG to /root/destination//IMG_0897.JPG
root#precise32:/vagrant# sh JPG_moves.sh /root/dir2/ /root/destination/
Copying /root/dir2/IMG_0897.JPG to /root/destination//IMG_0897.DUP.JPG

Check if passed argument is file or directory in Bash

I'm trying to write an extremely simple script in Ubuntu which would allow me to pass it either a filename or a directory, and be able to do something specific when it's a file, and something else when it's a directory. The problem I'm having is when the directory name, or probably files too, has spaces or other escapable characters are in the name.
Here's my basic code down below, and a couple tests.
#!/bin/bash
PASSED=$1
if [ -d "${PASSED}" ] ; then
echo "$PASSED is a directory";
else
if [ -f "${PASSED}" ]; then
echo "${PASSED} is a file";
else
echo "${PASSED} is not valid";
exit 1
fi
fi
And here's the output:
andy#server~ $ ./scripts/testmove.sh /home/andy/
/home/andy/ is a directory
andy#server~ $ ./scripts/testmove.sh /home/andy/blah.txt
/home/andy/blah.txt is a file
andy#server~ $ ./scripts/testmove.sh /home/andy/blah\ with\ a\ space.txt
/home/andy/blah with a space.txt is not valid
andy#server~ $ ./scripts/testmove.sh /home/andy\ with\ a\ space/
/home/andy with a space/ is not valid
All of those paths are valid, and exist.
That should work. I am not sure why it's failing. You're quoting your variables properly. What happens if you use this script with double [[ ]]?
if [[ -d $PASSED ]]; then
echo "$PASSED is a directory"
elif [[ -f $PASSED ]]; then
echo "$PASSED is a file"
else
echo "$PASSED is not valid"
exit 1
fi
Double square brackets is a bash extension to [ ]. It doesn't require variables to be quoted, not even if they contain spaces.
Also worth trying: -e to test if a path exists without testing what type of file it is.
At least write the code without the bushy tree:
#!/bin/bash
PASSED=$1
if [ -d "${PASSED}" ]
then echo "${PASSED} is a directory";
elif [ -f "${PASSED}" ]
then echo "${PASSED} is a file";
else echo "${PASSED} is not valid";
exit 1
fi
When I put that into a file "xx.sh" and create a file "xx sh", and run it, I get:
$ cp /dev/null "xx sh"
$ for file in . xx*; do sh "$file"; done
. is a directory
xx sh is a file
xx.sh is a file
$
Given that you are having problems, you should debug the script by adding:
ls -ld "${PASSED}"
This will show you what ls thinks about the names you pass the script.
Using -f and -d switches on /bin/test:
F_NAME="${1}"
if test -f "${F_NAME}"
then
echo "${F_NAME} is a file"
elif test -d "${F_NAME}"
then
echo "${F_NAME} is a directory"
else
echo "${F_NAME} is not valid"
fi
Using the "file" command may be useful for this:
#!/bin/bash
check_file(){
if [ -z "${1}" ] ;then
echo "Please input something"
return;
fi
f="${1}"
result="$(file $f)"
if [[ $result == *"cannot open"* ]] ;then
echo "NO FILE FOUND ($result) ";
elif [[ $result == *"directory"* ]] ;then
echo "DIRECTORY FOUND ($result) ";
else
echo "FILE FOUND ($result) ";
fi
}
check_file "${1}"
Output examples :
$ ./f.bash login
DIRECTORY FOUND (login: directory)
$ ./f.bash ldasdas
NO FILE FOUND (ldasdas: cannot open `ldasdas' (No such file or directory))
$ ./f.bash evil.php
FILE FOUND (evil.php: PHP script, ASCII text)
FYI: the answers above work but you can use -s to help in weird situations by checking for a valid file first:
#!/bin/bash
check_file(){
local file="${1}"
[[ -s "${file}" ]] || { echo "is not valid"; return; }
[[ -d "${file}" ]] && { echo "is a directory"; return; }
[[ -f "${file}" ]] && { echo "is a file"; return; }
}
check_file ${1}
Using stat
function delete_dir () {
type="$(stat --printf=%F "$1")"
if [ $? -ne 0 ]; then
echo "$1 directory does not exist. Nothing to delete."
elif [ "$type" == "regular file" ]; then
echo "$1 is a file, not a directory."
exit 1
elif [ "$type" == "directory" ]; then
echo "Deleting $1 directory."
rm -r "$1"
fi
}
function delete_file () {
type="$(stat --printf=%F "$1")"
if [ $? -ne 0 ]; then
echo "$1 file does not exist. Nothing to delete."
elif [ "$type" == "directory" ]; then
echo "$1 is a regular file, not a directory."
exit 1
elif [ "$type" == "regular file" ]; then
echo "Deleting $1 regular file."
rm "$1"
fi
}
https://linux.die.net/man/2/stat
https://en.m.wikipedia.org/wiki/Unix_file_types
A more elegant solution
echo "Enter the file name"
read x
if [ -f $x ]
then
echo "This is a regular file"
else
echo "This is a directory"
fi
Answer based on the title:
Check if passed argument is file or directory in Bash
This works also if the provided argument has a trailing slash .e.g. dirname/
die() { echo $* 1>&2; exit 1; }
# This is to remove the the slash at the end: dirName/ -> dirName
fileOrDir=$(basename "$1")
( [ -d "$fileOrDir" ] || [ -f "$fileOrDir" ] ) && die "file or directory $fileOrDir already exists"
Testing:
mkdir mydir
touch myfile
command dirName
# file or directory mydir already exists
command dirName/
# file or directory mydir already exists
command filename
# file or directory myfile already exists
#!/bin/bash
echo "Please Enter a file name :"
read filename
if test -f $filename
then
echo "this is a file"
else
echo "this is not a file"
fi
One liner
touch bob; test -d bob && echo 'dir' || (test -f bob && echo 'file')
result is true (0)(dir) or true (0)(file) or false (1)(neither)
This should work:
#!/bin/bash
echo "Enter your Path:"
read a
if [[ -d $a ]]; then
echo "$a is a Dir"
elif [[ -f $a ]]; then
echo "$a is the File"
else
echo "Invalid path"
fi

Resources