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

后端 未结 20 2468
深忆病人
深忆病人 2020-11-22 10:57

I\'ve used the following script to see if a file exists:

#!/bin/bash

FILE=$1     
if [ -f $FILE ]; then
   echo \"File $FILE exists.\"
else
   echo \"File $         


        
相关标签:
20条回答
  • 2020-11-22 11:24

    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
    
    0 讨论(0)
  • 2020-11-22 11:26

    The test command ([ here) has a "not" logical operator which is the exclamation point (similar to many other languages). Try this:

    if [ ! -f /tmp/foo.txt ]; then
        echo "File not found!"
    fi
    
    0 讨论(0)
  • 2020-11-22 11:29
    envfile=.env
    
    if [ ! -f "$envfile" ]
    then
        echo "$envfile does not exist"
        exit 1
    fi
    
    0 讨论(0)
  • 2020-11-22 11:30

    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
    
    0 讨论(0)
  • 2020-11-22 11:30

    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
    
    0 讨论(0)
  • 2020-11-22 11:31

    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).

    0 讨论(0)
提交回复
热议问题