[shell] Shell script to check if file exists

I'm trying to write a simple script that will tell me if a filename exist in $Temp that starts with the string "Test".

For example, I have these files

Test1989.txt
Test1990.txt
Test1991.txt

Then I just want to echo that a file was found.

For example, something like this:

file="home/edward/bank1/fiche/Test*"
if  test -s "$file" 
then 
    echo "found one"
else 
    echo "found none"
fi

But this doesn't work.

This question is related to shell

The answer is


The following script will help u to go to a process if that script exist in a specified variable,

cat > waitfor.csh

    #!/bin/csh

    while !( -e $1 )

    sleep 10m

    end

ctrl+D

here -e is for working with files,

$1 is a shell variable,

sleep for 10 minutes

u can execute the script by ./waitfor.csh ./temp ; echo "the file exits"


Wildcards aren't expanded inside quoted strings. And when wildcard is expanded, it's returned unchanged if there are no matches, it doesn't expand into an empty string. Try:

output="$(ls home/edward/bank1/fiche/Test* 2>/dev/null)"
if [ -n "$output" ]
then echo "Found one"
else echo "Found none"
fi

If the wildcard expanded to filenames, ls will list them on stdout; otherwise it will print an error on stderr, and nothing on stdout. The contents of stdout are assigned to output.

if [ -n "$output" ] tests whether $output contains anything.

Another way to write this would be:

if [ $(ls home/edward/bank1/fiche/Test* 2>/dev/null | wc -l) -gt 0 ]

One liner to check file exist or not -

awk 'BEGIN {print getline < "file.txt" < 0 ? "File does not exist" : "File Exists"}'

One approach:

(
  shopt -s nullglob
  files=(/home/edward/bank1/fiche/Test*)
  if [[ "${#files[@]}" -gt 0 ]] ; then
    echo found one
  else
    echo found none
  fi
)

Explanation:

  • shopt -s nullglob will cause /home/edward/bank1/fiche/Test* to expand to nothing if no file matches that pattern. (Without it, it will be left intact.)
  • ( ... ) sets up a subshell, preventing shopt -s nullglob from "escaping".
  • files=(/home/edward/bank1/fiche/Test*) puts the file-list in an array named files. (Note that this is within the subshell only; files will not be accessible after the subshell exits.)
  • "${#files[@]}" is the number of elements in this array.

Edited to address subsequent question ("What if i also need to check that these files have data in them and are not zero byte files"):

For this version, we need to use -s (as you did in your question), which also tests for the file's existence, so there's no point using shopt -s nullglob anymore: if no file matches the pattern, then -s on the pattern will be false. So, we can write:

(
  found_nonempty=''
  for file in /home/edward/bank1/fiche/Test* ; do
    if [[ -s "$file" ]] ; then
      found_nonempty=1
    fi
  done
  if [[ "$found_nonempty" ]] ; then
    echo found one
  else
    echo found none
  fi
)

(Here the ( ... ) is to prevent file and found_file from "escaping".)


for entry in "/home/loc/etc/"/*
do

   if [ -s /home/loc/etc/$entry ]
   then
       echo "$entry File is available"
   else
       echo "$entry File is not available"
fi
done

Hope it helps

You can do it in one line:

ls /home/edward/bank1/fiche/Test* >/dev/null 2>&1 && echo "found one" || echo "found none"

To understand what it does you have to decompose the command and have a basic awareness of boolean logic.

Directly from bash man page:

[...]
expression1 && expression2
     True if both expression1 and expression2 are true.
expression1 || expression2
     True if either expression1 or expression2 is true.
[...]

In the shell (and in general in unix world), the boolean true is a program that exits with status 0.

ls tries to list the pattern, if it succeed (meaning the pattern exists) it exits with status 0, 2 otherwise (have a look at ls man page for details).

In our case there are actually 3 expressions, for the sake of clarity I will put parenthesis, although they are not needed because && has precedence on ||:

 (expression1 && expression2) || expression3

so if expression1 is true (ie: ls found the pattern) it evaluates expression2 (which is just an echo and will exit with status 0). In this case expression3 is never evaluate because what's on the left site of || is already true and it would be a waste of resources trying to evaluate what's on the right.

Otherwise, if expression1 is false, expression2 is not evaluated but in this case expression3 is.


You have to understand how Unix interprets your input.

The standard Unix shell interpolates environment variables, and what are called globs before it passes the parameters to your program. This is a bit different from Windows which makes the program interpret the expansion.

Try this:

 $ echo *

This will echo all the files and directories in your current directory. Before the echo command acts, the shell interpolates the * and expands it, then passes that expanded parameter back to your command. You can see it in action by doing this:

$ set -xv
$ echo *
$ set +xv

The set -xv turns on xtrace and verbose. Verbose echoes the command as entered, and xtrace echos the command that will be executed (that is, after the shell expansion).

Now try this:

$ echo "*"

Note that putting something inside quotes hides the glob expression from the shell, and the shell cannot expand it. Try this:

$ foo="this is the value of foo"
$ echo $foo
$ echo "$foo"
$ echo '$foo'

Note that the shell can still expand environment variables inside double quotes, but not in single quotes.

Now let's look at your statement:

file="home/edward/bank1/fiche/Test*"

The double quotes prevent the shell from expanding the glob expression, so file is equal to the literal home/edward/bank1/finche/Test*. Therefore, you need to do this:

file=/home/edward/bank1/fiche/Test*

The lack of quotes (and the introductory slash which is important!) will now make file equal to all files that match that expression. (There might be more than one!). If there are no files, depending upon the shell, and its settings, the shell may simply set file to that literal string anyway.

You certainly have the right idea:

 file=/home/edward/bank1/fiche/Test*
 if  test -s $file
    then 
        echo "found one"
    else 
       echo "found none"
 fi

However, you still might get found none returned if there is more than one file. Instead, you might get an error in your test command because there are too many parameters.

One way to get around this might be:

if ls /home/edward/bank1/finche/Test* > /dev/null 2>&1
then
    echo "There is at least one match (maybe more)!"
else
    echo "No files found"
fi

In this case, I'm taking advantage of the exit code of the ls command. If ls finds one file it can access, it returns a zero exit code. If it can't find one matching file, it returns a non-zero exit code. The if command merely executes a command, and then if the command returns a zero, it assumes the if statement as true and executes the if clause. If the command returns a non-zero value, the if statement is assumed to be false, and the else clause (if one is available) is executed.

The test command works in a similar fashion. If the test is true, the test command returns a zero. Otherwise, the test command returns a non-zero value. This works great with the if command. In fact, there's an alias to the test command. Try this:

 $ ls -li /bin/test /bin/[

The i prints out the inode. The inode is the real ID of the file. Files with the same ID are the same file. You can see that /bin/test and /bin/[ are the same command. This makes the following two commands the same:

if test -s $file
then
    echo "The file exists"
fi

if [ -s $file ]
then
    echo "The file exists"
fi