Check if file exists and whether it contains a specific string - shell

I want to check if file2.sh exists and also if a specific word, poet is part of the file. I use grep to create the variable used_var.
#!/bin/ksh
file_name=/home/file2.sh
used_var=`grep "poet" $file_name`
How can I check if used_var has some value?

Instead of storing the output of grep in a variable and then checking whether the variable is empty, you can do this:
if grep -q "poet" $file_name
then
echo "poet was found in $file_name"
fi
============
Here are some commonly used tests:
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
-h FILE
FILE exists and is a symbolic link (same as -L)
-r FILE
FILE exists and is readable
-s FILE
FILE exists and has a size greater than zero
-w FILE
FILE exists and is writable
-x FILE
FILE exists and is executable
-z STRING
the length of STRING is zero
Example:
if [ -e "$file_name" ] && [ ! -z "$used_var" ]
then
echo "$file_name exists and $used_var is not empty"
fi

if test -e "$file_name";then
...
fi
if grep -q "poet" $file_name; then
..
fi

test -e will test whether a file exists or not. The test command returns a zero value if the test succeeds or 1 otherwise.
Test can be written either as test -e or using []
[ -e "$file_name" ] && grep "poet" $file_name
Unless you actually need the output of grep you can test the return value as grep will return 1 if there are no matches and zero if there are any.
In general terms you can test if a string is non-empty using [ "string" ] which will return 0 if non-empty and 1 if empty

If you have the test binary installed or ksh has a matching built-in function, you could use it to perform your checks. Usually /bin/[ is a symbolic link to test:
if [ -e "$file_name" ]; then
echo "File exists"
fi
if [ -z "$used_var" ]; then
echo "Variable is empty"
fi

You should use the grep -q flag for quiet output. See the man pages below:
man grep output :
General Output Control
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero status
if any match is found, even if an error was detected. Also see the -s or
--no-messages option. (-q is specified by POSIX.)
This KornShell (ksh) script demos the grep quiet output and is a solution to your question.
grepUtil.ksh :
#!/bin/ksh
#Initialize Variables
file=poet.txt
var=""
dir=tempDir
dirPath="/"${dir}"/"
searchString="poet"
#Function to initialize variables
initialize(){
echo "Entering initialize"
echo "Exiting initialize"
}
#Function to create File with Input
#Params: 1}Directory 2}File 3}String to write to FileName
createFileWithInput(){
echo "Entering createFileWithInput"
orgDirectory=${PWD}
cd ${1}
> ${2}
print ${3} >> ${2}
cd ${orgDirectory}
echo "Exiting createFileWithInput"
}
#Function to create File with Input
#Params: 1}directoryName
createDir(){
echo "Entering createDir"
mkdir -p ${1}
echo "Exiting createDir"
}
#Params: 1}FileName
readLine(){
echo "Entering readLine"
file=${1}
while read line
do
#assign last line to var
var="$line"
done <"$file"
echo "Exiting readLine"
}
#Check if file exists
#Params: 1}File
doesFileExit(){
echo "Entering doesFileExit"
orgDirectory=${PWD}
cd ${PWD}${dirPath}
#echo ${PWD}
if [[ -e "${1}" ]]; then
echo "${1} exists"
else
echo "${1} does not exist"
fi
cd ${orgDirectory}
echo "Exiting doesFileExit"
}
#Check if file contains a string quietly
#Params: 1}Directory Path 2}File 3}String to seach for in File
doesFileContainStringQuiet(){
echo "Entering doesFileContainStringQuiet"
orgDirectory=${PWD}
cd ${PWD}${1}
#echo ${PWD}
grep -q ${3} ${2}
if [ ${?} -eq 0 ];then
echo "${3} found in ${2}"
else
echo "${3} not found in ${2}"
fi
cd ${orgDirectory}
echo "Exiting doesFileContainStringQuiet"
}
#Check if file contains a string with output
#Params: 1}Directory Path 2}File 3}String to seach for in File
doesFileContainString(){
echo "Entering doesFileContainString"
orgDirectory=${PWD}
cd ${PWD}${1}
#echo ${PWD}
grep ${3} ${2}
if [ ${?} -eq 0 ];then
echo "${3} found in ${2}"
else
echo "${3} not found in ${2}"
fi
cd ${orgDirectory}
echo "Exiting doesFileContainString"
}
#-----------
#---Main----
#-----------
echo "Starting: ${PWD}/${0} with Input Parameters: {1: ${1} {2: ${2} {3: ${3}"
#initialize #function call#
createDir ${dir} #function call#
createFileWithInput ${dir} ${file} ${searchString} #function call#
doesFileExit ${file} #function call#
if [ ${?} -eq 0 ];then
doesFileContainStringQuiet ${dirPath} ${file} ${searchString} #function call#
doesFileContainString ${dirPath} ${file} ${searchString} #function call#
fi
echo "Exiting: ${PWD}/${0}"
grepUtil.ksh Output :
user#foo /tmp
$ ksh grepUtil.ksh
Starting: /tmp/grepUtil.ksh with Input Parameters: {1: {2: {3:
Entering createDir
Exiting createDir
Entering createFileWithInput
Exiting createFileWithInput
Entering doesFileExit
poet.txt exists
Exiting doesFileExit
Entering doesFileContainStringQuiet
poet found in poet.txt
Exiting doesFileContainStringQuiet
Entering doesFileContainString
poet
poet found in poet.txt
Exiting doesFileContainString
Exiting: /tmp/grepUtil.ksh

Related

How to see file exit in bash [duplicate]

This checks if a file exists:
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File $FILE exists."
else
echo "File $FILE does not exist."
fi
How do I only check if the file does not exist?
The test command (written as [ here) has a "not" logical operator, ! (exclamation mark):
if [ ! -f /tmp/foo.txt ]; then
echo "File not found!"
fi
Bash File Testing
-b filename - Block special file
-c filename - Special character file
-d directoryname - Check for directory Existence
-e filename - Check for file existence, regardless of type (node, directory, socket, etc.)
-f filename - Check for regular file existence not a directory
-G filename - Check if file exists and is owned by effective group ID
-G filename set-group-id - True if file exists and is set-group-id
-k filename - Sticky bit
-L filename - Symbolic link
-O filename - True if file exists and is owned by the effective user id
-r filename - Check if file is a readable
-S filename - Check if file is socket
-s filename - Check if file is nonzero size
-u filename - Check if file set-user-id bit is set
-w filename - Check if file is writable
-x filename - Check if file is executable
How to use:
#!/bin/bash
file=./file
if [ -e "$file" ]; then
echo "File exists"
else
echo "File does not exist"
fi
A test expression can be negated by using the ! operator
#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
echo "File does not exist"
else
echo "File exists"
fi
Negate the expression inside test (for which [ is an alias) using !:
#!/bin/bash
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist"
fi
The relevant man page is man test or, equivalently, man [ -- or help test or help [ for the built-in bash command.
Alternatively (less commonly used) you can negate the result of test using:
if ! [ -f "$FILE" ]
then
echo "File $FILE does not exist"
fi
That syntax is described in "man 1 bash" in sections "Pipelines" and "Compound Commands".
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"
Also, it's possible that the file is a broken symbolic link, or a non-regular file, like e.g. a socket, device or fifo. For example, to add a check for broken symlinks:
if [[ ! -f $FILE ]]; then
if [[ -L $FILE ]]; then
printf '%s is a broken symlink!\n' "$FILE"
else
printf '%s does not exist!\n' "$FILE"
fi
fi
It's worth mentioning that if you need to execute a single command you can abbreviate
if [ ! -f "$file" ]; then
echo "$file"
fi
to
test -f "$file" || echo "$file"
or
[ -f "$file" ] || echo "$file"
I prefer to do the following one-liner, in POSIX shell compatible format:
$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"
$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"
For a couple of commands, like I would do in a script:
$ [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}
Once I started doing this, I rarely use the fully typed syntax anymore!!
To test file existence, the parameter can be any one of the following:
-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink
All the tests below apply to regular files, directories, and symlinks:
-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0
Example script:
#!/bin/bash
FILE=$1
if [ -f "$FILE" ]; then
echo "File $FILE exists"
else
echo "File $FILE does not exist"
fi
You can do this:
[[ ! -f "$FILE" ]] && echo "File doesn't exist"
or
if [[ ! -f "$FILE" ]]; then
echo "File doesn't exist"
fi
If you want to check for file and folder both, then use -e option instead of -f. -e returns true for regular files, directories, socket, character special files, block special files etc.
You should be careful about running test for an unquoted variable, because it might produce unexpected results:
$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1
The recommendation is usually to have the tested variable surrounded by double quotation marks:
#!/bin/sh
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist."
fi
In
[ -f "$file" ]
the [ command does a stat() (not lstat()) system call on the path stored in $file and returns true if that system call succeeds and the type of the file as returned by stat() is "regular".
So if [ -f "$file" ] returns true, you can tell the file does exist and is a regular file or a symlink eventually resolving to a regular file (or at least it was at the time of the stat()).
However if it returns false (or if [ ! -f "$file" ] or ! [ -f "$file" ] return true), there are many different possibilities:
the file doesn't exist
the file exists but is not a regular file (could be a device, fifo, directory, socket...)
the file exists but you don't have search permission to the parent directory
the file exists but that path to access it is too long
the file is a symlink to a regular file, but you don't have search permission to some of the directories involved in the resolution of the symlink.
... any other reason why the stat() system call may fail.
In short, it should be:
if [ -f "$file" ]; then
printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi
To know for sure that the file doesn't exist, we'd need the stat() system call to return with an error code of ENOENT (ENOTDIR tells us one of the path components is not a directory is another case where we can tell the file doesn't exist by that path). Unfortunately the [ command doesn't let us know that. It will return false whether the error code is ENOENT, EACCESS (permission denied), ENAMETOOLONG or anything else.
The [ -e "$file" ] test can also be done with ls -Ld -- "$file" > /dev/null. In that case, ls will tell you why the stat() failed, though the information can't easily be used programmatically:
$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed
At least ls tells me it's not because the file doesn't exist that it fails. It's because it can't tell whether the file exists or not. The [ command just ignored the problem.
With the zsh shell, you can query the error code with the $ERRNO special variable after the failing [ command, and decode that number using the $errnos special array in the zsh/system module:
zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
err=$ERRNO
case $errnos[err] in
("") echo exists, not a regular file;;
(ENOENT|ENOTDIR)
if [ -L "$file" ]; then
echo broken link
else
echo does not exist
fi;;
(*) syserror -p "can't tell: " "$err"
esac
fi
(beware the $errnos support was broken with some versions of zsh when built with recent versions of gcc).
There are three distinct ways to do this:
Negate the exit status with bash (no other answer has said this):
if ! [ -e "$file" ]; then
echo "file does not exist"
fi
Or:
! [ -e "$file" ] && echo "file does not exist"
Negate the test inside the test command [ (that is the way most answers before have presented):
if [ ! -e "$file" ]; then
echo "file does not exist"
fi
Or:
[ ! -e "$file" ] && echo "file does not exist"
Act on the result of the test being negative (|| instead of &&):
Only:
[ -e "$file" ] || echo "file does not exist"
This looks silly (IMO), don't use it unless your code has to be portable to the Bourne shell (like the /bin/sh of Solaris 10 or earlier) that lacked the pipeline negation operator (!):
if [ -e "$file" ]; then
:
else
echo "file does not exist"
fi
envfile=.env
if [ ! -f "$envfile" ]
then
echo "$envfile does not exist"
exit 1
fi
To reverse a test, use "!".
That is equivalent to the "not" logical operator in other languages. Try this:
if [ ! -f /tmp/foo.txt ];
then
echo "File not found!"
fi
Or written in a slightly different way:
if [ ! -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or you could use:
if ! [ -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or, presing all together:
if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi
Which may be written (using then "and" operator: &&) as:
[ ! -f /tmp/foo.txt ] && echo "File not found!"
Which looks shorter like this:
[ -f /tmp/foo.txt ] || echo "File not found!"
The test thing may count too. It worked for me (based on Bash Shell: Check File Exists or Not):
test -e FILENAME && echo "File exists" || echo "File doesn't exist"
This code also working .
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File '$FILE' Exists"
else
echo "The File '$FILE' Does Not Exist"
fi
The simplest way
FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
This shell script also works for finding a file in a directory:
echo "enter file"
read -r a
if [ -s /home/trainee02/"$a" ]
then
echo "yes. file is there."
else
echo "sorry. file is not there."
fi
sometimes it may be handy to use && and || operators.
Like in (if you have command "test"):
test -b $FILE && echo File not there!
or
test -b $FILE || echo File there!
If you want to use test instead of [], then you can use ! to get the negation:
if ! test "$FILE"; then
echo "does not exist"
fi
You can also group multiple commands in the one liner
[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )
or
[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}
If filename doesn't exit, the output will be
test1
test2
test3
Note: ( ... ) runs in a subshell, { ... ;} runs in the same shell.

UNIX How do delete my unnecessary/excessive lines in my output?

My script is a mimic of the rm command, long story short. Can anyone point out the errors/unnecessary lines I have in my remove script that causes my output to produce excessive/irrelevant lines? The code works as intended but it produces all these unnecessary/excessive/duplicate lines. Output below is what looks like when I try to remove 2 files in the same line and do some other simple commands. Thank you in advance. I appreciate any help.
input: sh remove file2 file4
output:
Executed
Executed
cannot remove file4: no such file or directory
stat: cannot stat 'file4': No such file or directory
mv: cannot stat 'file4': No such file or directory
Executed
mv: cannot stat 'file4': No such file or directory
File moved to recycle bin
#/bin/bash
function directory(){
if [ ! -d ~/deleted ]
then
mkdir ~/deleted
fi
if [ ! -f ~/.restore.info ]
then
touch ~/.restore.info
fi
}
function movefile(){
mv $1 ~/deleted/$1
echo "file moved to recycle bin"
}
function error_conditions(){
#prints error messages and checks if file is in project directory
if [ ! -f ~/project ]
then
echo "cannot remove $filename: no such file or directory"
elif [ -d ~/project ]
then
echo "cannot remove $filename: is a directory"
else
echo "missing operand"
fi
}
function delete_file(){
#gets inode for filename
inode=$(stat -c%i $filename)
filename=$1
#pwd=$(readlink -e$filename)
if $interactive
then
if [ $verbose = true ]; then
read -p "Are you sure you want to delete $filename?" i_input
if [ $i_input == "y" ] || [ $i_input == "Y" ]
then
mv $filename ~/delete/${filename}_inode
echo ${filename}_$inode:$pwd>>~/.restore.info
echo "$filename has been deleted"
else
echo "Nothing has been deleted"
fi
else
read -p "Are you sure you want to delete $filename?" i_input
if [ $i_input == "y" ] || [ $i_input == "Y" ];
then
mv $filename ~/deleted/${filename}_$inode
echo ${filename}_$inode:$pwd>>~/.restore.info
else
echo Aborted
fi
fi
elif $verbose
then
mv $filename ~/deleted/${filename}_inode
echo ${filename}_$inode:$inode:pwd>>~/.restore.info
echo "$filename has been deleted."
else
mv $filename ~/deleted/${filename}_$inode
echo ${filename}_$inode:$pwd>>~/.restore.info
echo Executed
fi
}
interactive=false
verbose=false
while getopts iv option
do
case $option in
i) interactive=true;;
v) verbose=true;;
esac
done
shift $[OPTIND-1]
for i in $*
do
filename=$i
baseline=$(basename $i)
if [ "$i" == "" ];
then
echo "No filename provided"
elif [ -d $filename ];
then
if [ ! $recursive = true ];
then
echo "Directory name provided, please provide a file"
fi
elif [ ! -f $filename ];
then
echo "File does not exist"
elif [ "$basefule" == "safe_rm" ]
then
echo "Attempting to delete safe_rm"
else
delete_file $filename
fi
done
#################################M A I N###############################
directory
error_conditions $*
delete_file $*
movefile $*
Please indent properly.
for i in $* should be for i in "$#" or simply for i.
In general, variables should be quoted
(e.g., "$1", "$i",
"$filename",
"$verbose", etc.)
$[expression] is obsolete. 
Use $((expression)).
Your main loop calls
delete_file $filename
(line 100).
Your delete_file function sets
filename=$1
(line 35),
which is somewhat redundant and therefore confusing.
You set baseline but never use it. 
You test (i.e., reference) $basefule without ever setting it. 
Are these meant to be the same variable?
The code says
if [ ! -f ~/project ]
then
echo "cannot remove $filename: no such file or directory"
︙
This is a very misleading message.
You have a big comment that says “M A I N”,
but the “main” code begins about 33 lines earlier.
The code doesfor i in $*
do
filename=$i # This is an example of terrible indenting.
︙
delete_file $filename
︙
donebut then, five lines later,delete_file $*
so you’re processing the files twice. 
So, even if delete_file succeeds the first time you call it,
the file will be gone when you call it a second time.
And, if you want to call a function (e.g., delete_file)
with all the arguments to the script,
you should use "$#" rather than $*.
And, if you’re going to call delete_file with a list of filenames,
then delete_file needs to iterate (loop) over those arguments. 
Your delete_file function only looks at $1.

sed command in Solaris server

I have many javascript files in my solaris server which have some debug, print and trace statements which I want to comment. There are hundreds of file like this.
I have found a script to do this but the problem is the script is removing the urhk_b2k_printRepos statement instead of commenting it. The script is as below:
if [ $# -ne 2 ]
then
echo "usage: prdel <script file name> <directory in which scripts are present>"
exit 1
fi
file=$1
dir=$2
if [ ! -s ${file} ]
then
echo "script file ${file} either does not exist or is empty (zero bytes)"
echo "Exiting..."
exit 1
fi
if [ ! -d ${dir} ]
then
echo "Invalid directory ${dir} entered"
echo "Exiting..."
exit 1
fi
cordir="./corrected"
prlogdir="./prlog"
if [ -d ${cordir} ]
then
echo "The corrected directory exist in the path, Please remove and run the tool again"
echo "Exiting..."
exit 1
else
mkdir ${cordir}
fi
if [ -d ${prlogdir} ]
then
echo "The prlog directory exist in the path, Please remove and run the tool again"
echo "Exiting..."
exit 1
else
mkdir ${prlogdir}
fi
errFile="$prlogdir/scr_err.rpt"
sucFile="$prlogdir/scr_suc.rpt"
Lines=`wc -l $file`
cntr=1
while [ $cntr -le $Lines ]
do
src=`head -$cntr $file|tail -1`
echo "$cntr. Processing $src...."
srcPath="${dir}/${src}"
if [ ! -s ${srcPath} ]
then
echo "Script file ${src} does not exist in the path given" >> ${errFile}
else
cp $srcPath $cordir/$src.tmp
srctemp="$cordir/$src.tmp"
srccor="$cordir/$src.corrected"
printcnt=`grep -ci "^[ ]*print" $srctemp`
if [ $printcnt -ne 0 ]
then
cat $srctemp|sed 's/^[ ]*[ ]*print[ ]*(/#print(/'|sed 's/^[ ]*[ ]*PRINT[ ]*(/#PRINT(/' > $srccor
mv $srccor $srctemp
fi
prreposcnt=`grep -ci "printrepos" $srctemp`
if [ $prreposcnt -ne 0 ]
then
cat $srctemp|sed 's/^.*urhk_b2k_printRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_B2k_PrintRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_B2k_printRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_b2k_PrintRepos.*/#Printrepos statement removed/' > $srccor
else
cp $srctemp $srccor
fi
echo "Script file $src correction is done" >> ${sucFile}
rm $srctemp
diff $srcPath $srccor >> $prlogdir/$src.diff.rpt
fi
cntr=`expr $cntr + 1`
done
echo "done"
I am completely new to shell scripting. Can anyone help me to modify this script to comment "urhk_b2k_printRepos" lines and also comment "TRACE ON" lines.

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

How do I tell if a file does not exist in Bash?

This checks if a file exists:
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File $FILE exists."
else
echo "File $FILE does not exist."
fi
How do I only check if the file does not exist?
The test command (written as [ here) has a "not" logical operator, ! (exclamation mark):
if [ ! -f /tmp/foo.txt ]; then
echo "File not found!"
fi
Bash File Testing
-b filename - Block special file
-c filename - Special character file
-d directoryname - Check for directory Existence
-e filename - Check for file existence, regardless of type (node, directory, socket, etc.)
-f filename - Check for regular file existence not a directory
-G filename - Check if file exists and is owned by effective group ID
-G filename set-group-id - True if file exists and is set-group-id
-k filename - Sticky bit
-L filename - Symbolic link
-O filename - True if file exists and is owned by the effective user id
-r filename - Check if file is a readable
-S filename - Check if file is socket
-s filename - Check if file is nonzero size
-u filename - Check if file set-user-id bit is set
-w filename - Check if file is writable
-x filename - Check if file is executable
How to use:
#!/bin/bash
file=./file
if [ -e "$file" ]; then
echo "File exists"
else
echo "File does not exist"
fi
A test expression can be negated by using the ! operator
#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
echo "File does not exist"
else
echo "File exists"
fi
Negate the expression inside test (for which [ is an alias) using !:
#!/bin/bash
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist"
fi
The relevant man page is man test or, equivalently, man [ -- or help test or help [ for the built-in bash command.
Alternatively (less commonly used) you can negate the result of test using:
if ! [ -f "$FILE" ]
then
echo "File $FILE does not exist"
fi
That syntax is described in "man 1 bash" in sections "Pipelines" and "Compound Commands".
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"
Also, it's possible that the file is a broken symbolic link, or a non-regular file, like e.g. a socket, device or fifo. For example, to add a check for broken symlinks:
if [[ ! -f $FILE ]]; then
if [[ -L $FILE ]]; then
printf '%s is a broken symlink!\n' "$FILE"
else
printf '%s does not exist!\n' "$FILE"
fi
fi
It's worth mentioning that if you need to execute a single command you can abbreviate
if [ ! -f "$file" ]; then
echo "$file"
fi
to
test -f "$file" || echo "$file"
or
[ -f "$file" ] || echo "$file"
I prefer to do the following one-liner, in POSIX shell compatible format:
$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"
$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"
For a couple of commands, like I would do in a script:
$ [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}
Once I started doing this, I rarely use the fully typed syntax anymore!!
To test file existence, the parameter can be any one of the following:
-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink
All the tests below apply to regular files, directories, and symlinks:
-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0
Example script:
#!/bin/bash
FILE=$1
if [ -f "$FILE" ]; then
echo "File $FILE exists"
else
echo "File $FILE does not exist"
fi
You can do this:
[[ ! -f "$FILE" ]] && echo "File doesn't exist"
or
if [[ ! -f "$FILE" ]]; then
echo "File doesn't exist"
fi
If you want to check for file and folder both, then use -e option instead of -f. -e returns true for regular files, directories, socket, character special files, block special files etc.
You should be careful about running test for an unquoted variable, because it might produce unexpected results:
$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1
The recommendation is usually to have the tested variable surrounded by double quotation marks:
#!/bin/sh
FILE=$1
if [ ! -f "$FILE" ]
then
echo "File $FILE does not exist."
fi
In
[ -f "$file" ]
the [ command does a stat() (not lstat()) system call on the path stored in $file and returns true if that system call succeeds and the type of the file as returned by stat() is "regular".
So if [ -f "$file" ] returns true, you can tell the file does exist and is a regular file or a symlink eventually resolving to a regular file (or at least it was at the time of the stat()).
However if it returns false (or if [ ! -f "$file" ] or ! [ -f "$file" ] return true), there are many different possibilities:
the file doesn't exist
the file exists but is not a regular file (could be a device, fifo, directory, socket...)
the file exists but you don't have search permission to the parent directory
the file exists but that path to access it is too long
the file is a symlink to a regular file, but you don't have search permission to some of the directories involved in the resolution of the symlink.
... any other reason why the stat() system call may fail.
In short, it should be:
if [ -f "$file" ]; then
printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi
To know for sure that the file doesn't exist, we'd need the stat() system call to return with an error code of ENOENT (ENOTDIR tells us one of the path components is not a directory is another case where we can tell the file doesn't exist by that path). Unfortunately the [ command doesn't let us know that. It will return false whether the error code is ENOENT, EACCESS (permission denied), ENAMETOOLONG or anything else.
The [ -e "$file" ] test can also be done with ls -Ld -- "$file" > /dev/null. In that case, ls will tell you why the stat() failed, though the information can't easily be used programmatically:
$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed
At least ls tells me it's not because the file doesn't exist that it fails. It's because it can't tell whether the file exists or not. The [ command just ignored the problem.
With the zsh shell, you can query the error code with the $ERRNO special variable after the failing [ command, and decode that number using the $errnos special array in the zsh/system module:
zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
err=$ERRNO
case $errnos[err] in
("") echo exists, not a regular file;;
(ENOENT|ENOTDIR)
if [ -L "$file" ]; then
echo broken link
else
echo does not exist
fi;;
(*) syserror -p "can't tell: " "$err"
esac
fi
(beware the $errnos support was broken with some versions of zsh when built with recent versions of gcc).
There are three distinct ways to do this:
Negate the exit status with bash (no other answer has said this):
if ! [ -e "$file" ]; then
echo "file does not exist"
fi
Or:
! [ -e "$file" ] && echo "file does not exist"
Negate the test inside the test command [ (that is the way most answers before have presented):
if [ ! -e "$file" ]; then
echo "file does not exist"
fi
Or:
[ ! -e "$file" ] && echo "file does not exist"
Act on the result of the test being negative (|| instead of &&):
Only:
[ -e "$file" ] || echo "file does not exist"
This looks silly (IMO), don't use it unless your code has to be portable to the Bourne shell (like the /bin/sh of Solaris 10 or earlier) that lacked the pipeline negation operator (!):
if [ -e "$file" ]; then
:
else
echo "file does not exist"
fi
envfile=.env
if [ ! -f "$envfile" ]
then
echo "$envfile does not exist"
exit 1
fi
To reverse a test, use "!".
That is equivalent to the "not" logical operator in other languages. Try this:
if [ ! -f /tmp/foo.txt ];
then
echo "File not found!"
fi
Or written in a slightly different way:
if [ ! -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or you could use:
if ! [ -f /tmp/foo.txt ]
then echo "File not found!"
fi
Or, presing all together:
if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi
Which may be written (using then "and" operator: &&) as:
[ ! -f /tmp/foo.txt ] && echo "File not found!"
Which looks shorter like this:
[ -f /tmp/foo.txt ] || echo "File not found!"
The test thing may count too. It worked for me (based on Bash Shell: Check File Exists or Not):
test -e FILENAME && echo "File exists" || echo "File doesn't exist"
This code also working .
#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
echo "File '$FILE' Exists"
else
echo "The File '$FILE' Does Not Exist"
fi
The simplest way
FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
This shell script also works for finding a file in a directory:
echo "enter file"
read -r a
if [ -s /home/trainee02/"$a" ]
then
echo "yes. file is there."
else
echo "sorry. file is not there."
fi
sometimes it may be handy to use && and || operators.
Like in (if you have command "test"):
test -b $FILE && echo File not there!
or
test -b $FILE || echo File there!
If you want to use test instead of [], then you can use ! to get the negation:
if ! test "$FILE"; then
echo "does not exist"
fi
You can also group multiple commands in the one liner
[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )
or
[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}
If filename doesn't exit, the output will be
test1
test2
test3
Note: ( ... ) runs in a subshell, { ... ;} runs in the same shell.

Resources