while loop for checking a file name exists or not - shell

filename=""
while [[ $filename = "" ]];do
echo "Enter the file name"
read filename
if [[ -f $filename ]];then
echo "$filename exists"
else
echo "File name doesn't exists, so re-enter"
fi
done
with this style of loop, the loop exists when the file name is not present.
But i want the loop to continue until the user input the file name.
Please suggest what additional command i should include in the above script.

Use break to get out of the loop
while true ; do
echo "Enter the file name"
read filename
if [ -f "${filename}" ]; then
echo "${filename} exists"
break
else
echo "File name doesn't exists, so re-enter"
fi
done
echo "Thanks for ${filename}"
or shorter
while true ; do
read -p "Enter the file name: " filename
test -f "${filename}" && break
echo "File name doesn't exists, so re-enter"
done
echo "Thanks for ${filename}"

Quick and dirty way of doing it while utilizing the same code you have is simply adding your code inside an infinite loop.
while :
do
filename=""
while [[ $filename = "" ]];do
echo "Enter the file name"
read filename
if [[ -f $filename ]];then
echo "$filename exists"
else
echo "File name doesn't exists, so re-enter"
fi
done
done
Keep in mind that this will only look within the directory that the program is executed from unless a full path is given.

Related

How do I revert in case of wrong option?

select_again_dir:
echo -e "\033[32m enter the name of the source file; \033[0m"
echo
ls
echo
read build_source_dir
if [ ! -d $build_source_dir ]
then
echo "Folder Not Found. Please select again;"
goto select_again_dir:
else
echo "folder exists"
fi
Hello,
If the source file cannot be found, I want it to re-enter a value.
Is there a function you can suggest instead of goto?
The standard way is a while loop. One way to avoid duplication of the input read:
echo -e "\033[32m enter the name of the source file; \033[0m"
while [ ! -d "$build_source_dir" ]; do
echo
ls
echo
read build_source_dir
echo "Folder Not Found. Please select again: "
done
echo "folder exists"
Just loop it. You can use break and continue to your favor.
while true; do
echo -e "\033[32m enter the name of the source file; \033[0m"
echo
ls
echo
read build_source_dir
if [ ! -d "$build_source_dir" ]
then
echo "Folder Not Found. Please select again;"
continue # not really needed, there is nothing below to happen
else
echo "folder exists"
break # jump out of the loop
fi
done

How to Ask User for Confirmation: Shell

I am new to shell, and my code takes two arguments from the user. I would like to confirm their arguments before running the rest of the code. I would like a y for yes to prompt the code, and if they type n for no, then the code will ask again for new arguments
Pretty much, if i type anything when I am asked to confirm, the rest of the code runs anyways. I tried inserting the rest of the code after the first then statement, but that didn't work either. I have also checked my code with ShellCheck and it all appears to be legal syntax. Any advice?
#!/bin/bash
#user passes two arguments
echo "Enter source file name, and the number of copies: "
read -p "Your file name is $1 and the number of copies is $2. Press Y for yes N for no " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
echo "cloning files...."
fi
#----------------------------------------REST OF CODE
DIR="."
function list_files()
{
if ! test -d "$1"
then echo "$1"; return;
fi
cd ... || $1
echo; echo "$(pwd)":; #Display Directory name
for i in *
do
if test -d "$i" #if dictionary
then
list_files "$i" #recursively list files
cd ..
else
echo "$i"; #Display File name
fi
done
}
if [ $# -eq 0 ]
then list_files .
exit 0
fi
for i in "$#*"
do
DIR=$1
list_files "$DIR"
shift 1 #To read next directory/file name
done
if [ ! -f "$1" ]
then
echo "File $1 does not exist"
exit 1
fi
for ((i=0; i<$2; i++))
do
cp "$1" "$1$i.txt"; #copies the file i amount of times, and creates new files with names that increment by 1
done
status=$?
if [ "$status" -eq 0 ]
then
echo 'File copied succeaful'
else
echo 'Problem copying'
fi
Moving the prompts into a while loop might help here. The loop will re-prompt for the values until the user confirms them. Upon confirmation, the target code will be executed and the break statement will terminate the loop.
while :
do
echo "Enter source file name:"
read source_file
echo "Number of copies"
read number_of_copies
echo "Your file name is $source_file and the number of copies is $number_of_copies."
read -p "Press Y for yes N for no " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "cloning files...."
break ### <<<---- terminate the loop
fi
echo ""
done
#----------------------------------------REST OF CODE

if [ `ls $op_file` ]; then is returning true even the file is not found

#!/bin/bash
echo "Enter the o/p file name"
read op_file
echo "Enter the count"
read count
echo "OP filename : "
echo $op_file
if [ `ls $op_file` ]; then
echo "O/P file found"
else
exit 0
fi
I'm trying to check whether the filename is existing or not.Have to proceed if the file is existing. Though the above code doesn't give me error. It prints O/P file found even though the file is not found by ls.
Your script can be refactored to this:
#!/bin/bash
read -p "Enter the o/p file name" op_file
read -p "Enter the count" count
echo "OP filename: $op_file"
if [ -f "$op_file" ]; then
echo "O/P file found"
else
exit 1
fi
Better to use -f "$file" check for checking existence of a file. Also see man test
Use the -e operator in your if, this checks the for existence of a file, so:
if [ -e $op_file ]; then
echo "O/P file found"
else
exit 0
fi

two bash functions that need to be fixed

I need some help getting this running. just two sections.
I'm a novice at bash scripting. how do I repeat this until a single filename is entered? PS it doens't seem to work with a while loop so far that I've tried.
ifSpaces(){
#if more than one file name if statement(checks for spaces)
if [ "$#" -eq "$(echo "$#" | wc -w)" ]
then
echo -n "Please enter a filename: ";
read filename
else
echo -n "Please enter a single filename! ";
fi
}
this function is supposed to test the file if it can be written. It seems to pass through it and not test it right. but I'm not really sure. Basically, what's wrong with it, please make corrections because I don't get it when people tell me and not show me how.
#how do I get this to work?
testFiles(){
#loop through files and test each one
for filename in "$#"
do
filename="$#"
# put this in a loop that grabs all the values.
# test all the file names
while [ -f "$filename" ]
do
if [ -w $filename ]
then
echo "The file exists and is writable";
overWriteFile
saveResults
elif [ -d $filename ]
then
read filename
echo "$filename";
echo "The file you specified exists and is a directory".
saveResults
else
>$directory$filename;
fi
done
echo "$filename";
echo "The file you specified exists and is neither a regular file nor a directory.";
done
saveResults
}
The ifSpaces() function needs better definition - what is it to do?
Takes one argument, a possible file name.
If that argument contains no spaces, return it.
If that argument contains spaces, prompt for a new file name until a name without spaces is given.
How to test for spaces in a string (aka filename)?
spacesInName()
{
case "$1" in
(* *) return 0;;
(*) return 1;;
esac
}
Hence:
ifSpaces()
{
filename=$1
while spacesInName "$filename"
do
echo -n "Enter a filename without spaces: "
read filename
done
return $filename
}
OK - that done, now we ask "why?". Wouldn't it be better just to make sure your code works with any valid file name, because all the main operating systems (file systems) recognize file names with spaces in them as valid?
Additionally, shell scripts that go prompting are often rather uncivilized; they certainly cannot be used reliably when there isn't a user to provide the input, and they cannot be used reliably in pipelines of commands. That severely limits their usefulness.
So, general purpose scripts don't ask questions. Special purpose scripts can ask questions. And it does depend on what the script is designed to do and who is going to use it. But avoid chattiness whenever possible.
You second function is similarly very confused. It isn't very clear what it is supposed to do, but this looks more plausible than the original:
testFiles()
{
for filename in "$#"
do
if [ -w "$filename" ]
then
echo "The file $filename exists and is writable";
saveResults "$filename"
elif [ -d "$filename" ]
then
echo "The file $filename exists and is a directory".
elif [ -f "$filename" ]
then
echo "The file $filename exists but is not writable"
else
echo "Either $filename does not exist or it is neither"
echo "a file nor a directory"
fi
done
}
Revised to prompt once per missing file for an answer - create the file...
testFiles()
{
for filename in "$#"
do
if [ -w "$filename" ]
then
echo "The file $filename exists and is writable";
saveResults "$filename"
elif [ -d "$filename" ]
then
echo "The file $filename exists and is a directory".
elif [ -f "$filename" ]
then
echo "The file $filename exists but is not writable"
elif [ ! -e "$filename ]
then
echo "$filename does not exist - create it? "
read yesno
case "$yesno" in
([Yy]*) cp /dev/null "$filename"
saveResults "$filename"
;;
(*) echo "OK - ignoring $filename"
;;
esac
fi
done
}

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