如何判断Bash中是否不存在常规文件?


3260

我使用以下脚本来查看文件是否存在:

#!/bin/bash

FILE=$1     
if [ -f $FILE ]; then
   echo "File $FILE exists."
else
   echo "File $FILE does not exist."
fi

如果我只想检查文件是否存在,使用的正确语法是什么?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi

187
我发现此bash条件语句列表非常有用。
SauliusŽemaitaitis09年

9
作为我非常懒惰的人,我通常会使用以下愚蠢的变通办法:if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;可能很好,我找到了这个问题,并学会了以更适当的方式进行操作。:)
Alderath

5
要悬而未决,您应该说“常规文件”,因为大多数UNIX / POSIX文档通常将所有类型的文件系统条目简称为“文件”,例如,符号链接是文件的类型,命名管道也是如此。 ,常规文件,目录,特殊块,特殊字符,套接字等
kevinarpe13年

11
@kevinarpe如果要测试是否存在某些东西,请使用-e。-f将不会提取目录,符号链接等
。– Benubird

14
为了安全起见,请始终使用双引号正确处理带空格的文件名,例如FILE=$1-> FILE="$1"if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Answers:


4519

测试命令([在这里)有一个“非”逻辑运算符是感叹号(类似于许多其他语言)。尝试这个:

if [ ! -f /tmp/foo.txt ]; then
    echo "File not found!"
fi

207
更简洁地说:[!-f /tmp/foo.txt] &&回显“找不到文件!”
DavidWinterbottom 2010年

38
我为“如果2个文件中的任何一个不存在”找到正确的语法有点费劲。以下两项工作:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk 2012年

153
@DavidWinterbottom更加鲜活:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.

27
参数可以是以下任意一项:-e: Returns true value, if file exists -f: Return true value, if file exists and regular file -r: Return true value, if file exists and is readable -w: Return true value, if file exists and is writable -x: Return true value, if file exists and is executable -d: Return true value, if exists and is a directory
SD。

5
! -f与with搭配&&使用-f与with搭配使用存在不对称性||。这与不存在/存在检查返回的退出代码有关。如果您希望行始终以退出代码0干净地退出(有时您不希望此约束),则这两种方法不可互换。另外,只需使用一条if语句,您就不必担心不存在/存在检查的退出代码。
Acumenus

670

Bash文件测试

-b filename-阻止特殊文件
-c filename-特殊字符文件
-d directoryname-检查目录是否存在
-e filename-检查文件是否存在,而与类型(节点,目录,套接字等)无关
-f filename-检查常规文件是否存在而不是目录
-G filename-检查文件是否存在并归谁所有有效的组ID-
-G filename set-group-id如果文件存在且为set-group-id,则为
-k filenametrue-粘性位
-L filename-符号链接
-O filename-如果文件存在且为有效用户ID所拥有,则为true-
-r filename检查文件是否可读
-S filename-检查文件是否为套接字
-s filename-检查是否文件大小不为零
-u filename-检查文件set-user-id位是否设置
-w filename-检查文件是否可写
-x filename-检查文件是否可执行

如何使用:

#!/bin/bash
file=./file
if [ -e "$file" ]; then
    echo "File exists"
else 
    echo "File does not exist"
fi 

测试表达可以通过使用被否定!操作者

#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
    echo "File does not exist"
else 
    echo "File exists"
fi 

1
@ 0x90如果需要,您可以随意编辑我的帖子并将其添加到列表中。我猜你的意思是:-n String-检查字符串的长度是否不为零。还是您的意思是file1 -nt file2-检查file1是否比文件2更新(您也可以使用-ot代表更旧的文件)
BlueCacti 2014年

1
关于-n: The unary operator -z tests for a null string, while -n or no operator at all returns True if a string is not empty.ibm.com/developerworks/library/l-bash-test/index.html
BlueCacti

1
为什么有些人没有添加像function exist(){⏎如果[-e“ $ 1”]这样的函数;然后回显“ $ 1存在”,否则回显“ $ 1不存在” fi}
Mz A

291

您可以使用“!”取反表达式:

#!/bin/bash
FILE=$1

if [ ! -f "$FILE" ]
then
    echo "File $FILE does not exist"
fi

相关手册页是man test或等效地man [- help testhelp [对于内置bash命令。


4
bash中[是一个内置函数。因此,相关信息是通过help [... 来获取的,但这表明它[test内建函数的同义词,因此,相关信息是通过来获取的help test。另请参见手册中Bash条件表达式部分
gniourf_gniourf 2013年

@gniourf_gniourf:是的,但是bash内置[命令的行为与外部[命令非常相似,因此,要么man test或者man [将使您对它的工作原理有所了解。
Keith Thompson

8
@KeithThompson除了bash内置的[开关比[我的系统上发现的外部命令更多外……一般而言,我认为最好阅读特定于给定工具的文档,而不是特定于另一模糊关联的文档。不过,我可能错了;)
gniourf_gniourf 2013年

134
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

同样,文件可能是断开的符号链接,也可能是非常规文件,例如套接字,设备或FIFO。例如,要添加对损坏的符号链接的检查:

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

9
请问为什么测试中要用两个“ [”?(例如[[!!-a $ FILE]]。我尝试了在solaris盒子上提到的所有选项,但只有一个选项起作用了,非常感谢,但是为什么呢?
Dimitrios Mistriotis 2011年

30
双括号是“现代”扩展名。例如,他们不会进行单词拆分(例如,带空格的文件名),并且仍然适用于空字符串:mywiki.wooledge.org/BashFAQ/031
bw1024

7
根据tldp.org/LDP/abs/html/fto.html的说明 -a与-e的作用相同。它已被“弃用”,不建议使用。无论如何+1提及也要检查损坏的符号链接
Luca Borrione 2012年

4
@dimitrismistriotis两个“ [”是由zsh&bash(不同)实现的非便携式扩展名;通常,您应尽可能避免使用它。
好人2012年

7
我之所以投票,是因为它使用了[[。它是一种广泛使用的扩展。如果您知道您正在使用bash,则没有理由不使用它。它比[容易出错。
Michael Potter


69

我更喜欢以POSIX Shell兼容格式执行以下单行代码:

$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"

$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"

对于几个命令,就像我在脚本中那样:

$  [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}

一旦开始执行此操作,就很少再使用全类型语法了!!


1
首先,未加引号的变量引用容易出错。就是说,在任何 bash联机帮助页中,默认情况下(而不是),内置[test内建函数将测试参数文件是否存在-e?那不是模棱两可的吗?AFAIK(和AIUI的“条件表达式”部分)唯一测试过的方法是参数不为空(或未定义),在这种情况下,即重言式(let $DIR = ''$FILE = '',则参数仍然为'//')。
PointedEars

1
证明:ls /foo,结果ls: cannot access /foo: No such file or directory[ /foo ] && echo 42结果42。GNU bash版本4.2.37(1)-发行版(i486-pc-linux-gnu)。
PointedEars 2012年

@PointedEars:-f在编写此答案时,我未能指定选项。显然-e,如果不确定该文件将是常规文件,则始终可以使用。另外,在我所有的脚本中,我都引用了这些构造,我必须在没有充分证明的情况下才提交了此构造。
JM Becker

确认 但是您可能知道,单线无法解决if-else问题:[ $condition ] && if_true || if_false容易出错。无论如何,我发现比[ ! -f "$file" ] && if_not_exists容易阅读和理解[ -f "$file" ] || if_not_exists
PointedEars 2012年

55

要测试文件是否存在,参数可以是以下任意一项:

-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

以下所有测试均适用于常规文件,目录和符号链接:

-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

示例脚本:

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi

39

你可以这样做:

[[ ! -f "$FILE" ]] && echo "File doesn't exist"

要么

if [[ ! -f "$FILE" ]]; then
    echo "File doesn't exist"
fi

如果要同时检查文件和文件夹,请使用-eoption代替-f-e对于常规文件,目录,套接字,字符特殊文件,块特殊文件等,返回true。


1
那不是bash特有的。该语法来自Korn Shell,并且在zsh和bash中也可用。但是,与标准[实用程序相比,它的优势有限。
Stephane Chazelas

常规文件(由检查-f)和目录只是许多不同类型文件中的两种。还有套接字,符号链接,设备,FIFO,门... [ -e在符号链接解析后测试文件是否存在(包括常规,FIFO,目录等任何类型)。
Stephane Chazelas

@StephaneChazelas:我在任何地方都看不到任何ksh或zsh标签。我们谈论的是bash或大多数unix系统上标准化的东西。因此,在上下文中,它是特定于bash的。OP不需要知道那里有任何贝壳:D
Jahid

那是对您答案的“特定于重击”部分的评论。
Stephane Chazelas

@StephaneChazelas:是的,并且在上下文(bash和标准sh)中,它是bash特定的。我没有在您的第二条评论中指出要点,这完全是出于上下文。OP不需要-e,他需要-f
Jahid

35

您应该小心运行test未加引号的变量,因为它可能会产生意外的结果:

$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1

通常建议将测试的变量括在双引号中:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi

8
建议将每个变量都用双引号引起来,除非您确切地知道自己是不必要的一种罕见情况,或者是有害的一种罕见情况。(不,这不是其中之一。)
Uwe

您是否愿意详细说明为什么不使用双引号呢?否则,我看不到评论的用处。
artdanil

4
我的意思是:这不是不必要或有害的罕见情况之一。Shell程序员应该习惯于(几乎)将每个变量括在双引号中。这个规则不仅仅限于[ ... ]
Uwe


24

有三种不同的方法可以做到这一点:

  1. 用bash否定退出状态(没有其他答案说过):

    if ! [ -e "$file" ]; then
        echo "file does not exist"
    fi

    要么:

    ! [ -e "$file" ] && echo "file does not exist"
  2. 否定测试命令中的测试[(这是以前大多数答案提出的方式):

    if [ ! -e "$file" ]; then
        echo "file does not exist"
    fi

    要么:

    [ ! -e "$file" ] && echo "file does not exist"
  3. 根据测试结果是否定(||而不是)采取行动&&

    只要:

    [ -e "$file" ] || echo "file does not exist"

    这看起来很愚蠢(IMO),除非您的代码必须可移植到/bin/sh缺少管道求反运算符(!)的Bourne shell(如Solaris 10或更早版本)上,否则不要使用它:

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi

! [和之间[ !是否存在任何可移植性差异?
冰雪奇缘》 2015年

3
! [ 是POSIX壳管道2.9.2(任何命令) Otherwise, the exit status shall be the logical NOT of the exit status of the last command,并[ ! 为POSIX测试 ! expression True if expression is false. False if expression is true.所以,两者都是POSIX,在我的经验,它们都被广泛的支持。

1
@ FrozenFlame,Bourne shell没有!Korn shell引入的关键字。除了可能对于Solaris 10和更早的Solaris,这些天您不太可能遇到Bourne shell。
Stephane Chazelas

22

[ -f "$file" ]

[命令在存储的路径上执行stat()(不是lstat())系统调用,如果该系统调用成功并且返回的文件类型为“ regular ” ,则$file返回truestat()

因此,如果[ -f "$file" ]返回true,则可以判断该文件确实存在,并且是最终解析为常规文件的常规文件或符号链接(或至少在时是stat())。

但是如果返回错误(或者[ ! -f "$file" ]! [ -f "$file" ]返回true),也有很多不同的可能性:

  • 该文件不存在
  • 该文件存在但不是常规文件(可以是设备,FIFO,目录,套接字...)
  • 该文件存在,但是您没有对父目录的搜索权限
  • 该文件存在,但是访问该文件的路径太长
  • 该文件是与常规文件的符号链接,但是您没有对符号链接解析中涉及的某些目录的搜索权限。
  • ... stat()系统调用可能失败的任何其他原因。

简而言之,应该是:

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

为了确定文件不存在,我们需要stat()系统调用返回错误代码ENOENTENOTDIR告诉我们其中一个路径组件不是目录,这是另一种情况,我们可以告诉文件不存在通过该路径存在)。不幸的是,该[命令没有让我们知道这一点。无论错误代码是ENOENT,EACCESS(拒绝权限),ENAMETOOLONG还是其他任何值,它都将返回false。

[ -e "$file" ]测试也可以做ls -Ld -- "$file" > /dev/null。在这种情况下,尽管无法轻松地以编程方式使用这些信息,但ls会告诉您stat()失败的原因:

$ 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

至少ls告诉我这不是因为文件不存在而导致失败。这是因为它无法判断文件是否存在。该[命令只是忽略了该问题。

使用zshshell,您可以$ERRNO在失败的[命令后使用特殊变量查询错误代码,并使用模块中的$errnos特殊数组对该数字进行解码zsh/system

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

(请注意$errnoszsh当使用的最新版本构建时,某些版本gcc支持已被破坏)。


11

要撤消测试,请使用“!”。这等效于其他语言中的“非”逻辑运算符。尝试这个:

if [ ! -f /tmp/foo.txt ];
then
    echo "File not found!"
fi

或以稍微不同的方式编写:

if [ ! -f /tmp/foo.txt ]
    then echo "File not found!"
fi

或者您可以使用:

if ! [ -f /tmp/foo.txt ]
    then echo "File not found!"
fi

或者,全部保持一致:

if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi

可以这样写(使用“ and”运算符:&&):

[ ! -f /tmp/foo.txt ] && echo "File not found!"

看起来更短:

[ -f /tmp/foo.txt ] || echo "File not found!"


10

此代码也有效。

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi

7

最简单的方法

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"

6

此Shell脚本还可用于在目录中查找文件:

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

3
尚不清楚这会增加其他答案尚未给出的任何内容。它对路径名进行硬编码。由于该问题被标记为bash,因此可以read -p "Enter file name: " -r a用来提示和阅读。它确实在变量周围使用引号;很好,但是应该解释。如果回显文件名可能会更好。这会检查文件是否存在并且不为空(这是的含义-s),而问题将询问任何文件,无论是否为空(-f更合适)。
乔纳森·莱夫勒

4

有时使用&&和||可能很方便 操作员。

像在(如果您有命令“ test”):

test -b $FILE && echo File not there!

要么

test -b $FILE || echo File there!


0

您也可以将多个命令组合在一个衬纸中

[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )

要么

[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}

如果没有退出文件名,则输出为

test1
test2
test3

注意:(...)在子shell中运行,{...;}在同一shell中运行。大括号表示法仅适用于bash。


1
不可以,大括号表示法不是仅限bash使用;它可以在任何符合POSIX的外壳中使用。
查尔斯·达菲,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.