Câu hỏi Làm cách nào để biết một tệp thông thường không tồn tại trong Bash?


Tôi đã sử dụng tập lệnh sau để xem tệp có tồn tại không:

#!/bin/bash

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

Cú pháp chính xác để sử dụng là gì nếu tôi chỉ muốn kiểm tra xem tệp có không phải hiện hữu?

#!/bin/bash

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

2518
2018-03-12 14:48


gốc


Tôi đã tìm thấy điều này danh sách các câu lệnh điều kiện bash rất hữu dụng. - Saulius Žemaitaitis
Là một người rất lười biếng, tôi thường sử dụng cấu trúc giải quyết ngớ ngẩn sau đây: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi; Có lẽ tốt mà tôi tìm thấy câu hỏi này thay vào đó và học cách làm điều đó một cách thích hợp hơn. :) - Alderath
Để tạo mặt nạ, bạn nên nói "tệp thông thường", vì hầu hết các tài liệu UNIX / POSIX tham chiếu chung cho tất cả các loại mục hệ thống tệp chỉ đơn giản là "tệp", ví dụ: liên kết tượng trưng là loại tệp. , tệp thông thường, thư mục, khối đặc biệt, ký tự đặc biệt, ổ cắm, v.v. - kevinarpe
@ kevinarpe nếu bạn muốn kiểm tra xem một cái gì đó tồn tại, sử dụng -e. -f sẽ không nhận thư mục, liên kết tượng trưng, ​​v.v. - Benubird
Để an toàn, hãy luôn sử dụng dấu ngoặc kép để xử lý chính xác tên tệp có khoảng trắng, ví dụ: FILE=$1 -> FILE="$1" và if [ -f $FILE ]; -> if [ -f "$FILE" ]; - kevinarpe


Các câu trả lời:


Các kiểm tra chỉ huy ([ ở đây) có toán tử logic "không" là dấu chấm than (tương tự như nhiều ngôn ngữ khác). Thử cái này:

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

3529
2018-03-12 14:50



Tôi đã cố gắng một chút để tìm cú pháp đúng cho "nếu có 2 tệp không tồn tại". Cả hai công việc sau đây: if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi  if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi - mivk
@DavidWinterbottom Thậm chí còn mọng nước hơn: [ -f /tmp/foo.txt ] || echo "File not found!" - David W.
Tham số có thể là một trong những điều sau: -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.
Nếu bạn muốn sử dụng 2 điều kiện trong một hoạt động nếu sử dụng toán tử -a, điều này có nghĩa là AND. Tôi nghĩ rằng điều này cũng có thể xảy ra với & nếu bạn sử dụng toán tử [[]] - GroundZero
Có một bất đối xứng trong việc sử dụng ! -f với && so với sử dụng -f với ||. Điều này phải làm với mã thoát được trả về bởi kiểm tra không tồn tại. Nếu bạn cần dòng của bạn luôn luôn thoát sạch với mã thoát 0 (và đôi khi bạn không muốn ràng buộc này), hai cách tiếp cận không thể hoán đổi cho nhau. Ngoài ra, chỉ cần sử dụng if tuyên bố và bạn không còn phải lo lắng về mã thoát của kiểm tra không tồn tại của bạn. - A-B-B


Kiểm tra tệp Bash

-b filename - Chặn tệp đặc biệt
-c filename - Tệp ký tự đặc biệt
-d directoryname - Kiểm tra thư mục Existence
-e filename - Kiểm tra sự tồn tại của tệp, bất kể loại (nút, thư mục, ổ cắm, v.v.)
-f filename - Kiểm tra sự tồn tại của tệp thông thường không phải là thư mục
-G filename - Kiểm tra xem tệp có tồn tại không và thuộc sở hữu của ID nhóm hiệu quả
-G filename set-group-id - Đúng nếu tệp tồn tại và được đặt là nhóm-id
-k filename - Chú ý bit
-L filename - Liên kết tượng trưng
-O filename - Đúng nếu tệp tồn tại và được sở hữu bởi id người dùng hiệu quả
-r filename - Kiểm tra xem tệp có thể đọc được không
-S filename - Kiểm tra xem tệp có phải là ổ cắm không
-s filename - Kiểm tra xem tệp có kích thước không phải là không
-u filename - Kiểm tra xem tập tin tập tin-user-id bit được thiết lập
-w filename - Kiểm tra xem tệp có ghi được không
-x filename - Kiểm tra xem tệp có thực thi không

Cách sử dụng:

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

A biểu thức kiểm tra có thể bị từ chối bằng cách sử dụng ! nhà điều hành

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

429
2018-01-16 14:25



@ 0x90 Nếu bạn muốn, bạn có thể chỉnh sửa bài đăng của tôi và thêm nó vào danh sách. Tôi đoán bạn có nghĩa là: -n String - Kiểm tra xem độ dài của chuỗi không phải là số không. Hay ý bạn là file1 -nt file2 - Kiểm tra xem tập tin 1 có mới hơn thì tệp 2 (bạn cũng có thể dùng -ot cho cũ hơn) - GroundZero
Về -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 - GroundZero


Bạn có thể phủ nhận một biểu thức bằng "!":

#!/bin/bash
FILE=$1

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

Trang người dùng có liên quan là man test hoặc, tương đương, man [ -- hoặc là help test hoặc là help [ cho lệnh bash tích hợp.


250
2018-03-12 14:50



Trong bash, [ là một nội trang. Vì vậy, thông tin có liên quan được thu được bằng help [... nhưng điều này cho thấy [ là một từ đồng nghĩa cho test được xây dựng, do đó các thông tin có liên quan được thu được bởi help test. Xem thêm Phần Biểu thức điều kiện Bash trong hướng dẫn sử dụng. - gniourf_gniourf
@gniourf_gniourf: Có, nhưng bash được xây dựng trong [ lệnh hoạt động rất giống với bên ngoài [ lệnh, vì vậy man test hoặc là man [ sẽ cung cấp cho bạn ý tưởng tốt về cách hoạt động của nó. - Keith Thompson
@KeithThompson ngoại trừ việc bash được xây dựng trong [ có nhiều công tắc hơn lệnh ngoài [ tìm thấy trên hệ thống của tôi ... Nói chung tôi tin rằng tốt hơn là đọc tài liệu cụ thể cho một công cụ cụ thể, chứ không phải tài liệu cụ thể cho một tài liệu mơ hồ khác có liên quan. Tôi có thể sai mặc dù ;) - gniourf_gniourf


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

Ngoài ra, có thể tệp đó là một liên kết tượng trưng bị hỏng hoặc một tệp không thường xuyên, chẳng hạn như ví dụ: một ổ cắm, thiết bị hoặc nămo. Ví dụ: để thêm dấu kiểm cho các liên kết bị hỏng:

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

110
2018-03-12 14:52



Tôi có thể hỏi tại sao hai "[" trong thử nghiệm này? (ví dụ [[! -a $ FILE]]). Tôi đã thử tất cả các tùy chọn được đề cập trên một hộp năng lượng mặt trời và chỉ có một cái đã làm việc, rất biết ơn, nhưng tại sao? - Dimitrios Mistriotis
Dấu ngoặc kép là phần mở rộng "hiện đại"; ví dụ: họ sẽ không thực hiện tách từ (chẳng hạn như đối với tên tệp có dấu cách) và vẫn hoạt động đối với các chuỗi trống: mywiki.wooledge.org/BashFAQ/031 - bw1024
theo tldp.org/LDP/abs/html/fto.html -a là giống hệt nhau trong -e. Nó đã bị "phản đối" và việc sử dụng nó không được khuyến khích. Dù sao đi nữa cũng có đề cập đến việc kiểm tra liên kết bị hỏng - Luca Borrione
@dimitrismistriotis hai "[" là một phần mở rộng không di động được thực hiện (khác) bởi zsh & bash; nói chung bạn nên tránh nó nếu có thể. - Good Person
Tôi lên bầu chọn BECAUSE nó được sử dụng [[. Nó là một phần mở rộng được sử dụng rộng rãi. Nếu bạn biết bạn đang sử dụng bash thì không có lý do gì để không sử dụng nó. Đó là lỗi ít hơn nhiều so với [. - Michael Potter


Điều đáng nói đến là nếu bạn cần thực hiện một lệnh duy nhất, bạn có thể viết tắt

if [ ! -f "$file" ]; then
    echo "$file"
fi

đến

test -f "$file" || echo "$file"

hoặc là

[ -f "$file" ] || echo "$file"

80
2017-08-11 09:15



Cảm ơn! Cần một giải pháp thay thế không sử dụng [[]] - Jonathan


Tôi thích làm một lớp lót sau, trong POSIX shell định dạng tương thích:

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

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

Đối với một vài lệnh, như tôi sẽ làm trong một kịch bản:

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

Một khi tôi bắt đầu làm điều này, tôi hiếm khi sử dụng cú pháp gõ đầy đủ nữa !!


57
2017-10-18 16:09



Trước hết, các tham chiếu biến không được trích dẫn là dễ bị lỗi. Điều đó nói rằng, nó nói ở đâu bất kì bash manpage rằng [ hoặc là test tích hợp sẽ kiểm tra tập tin tồn tại của đối số theo mặc định (trái ngược với -e)? Điều đó sẽ không mơ hồ? AFAIK (và AIUI phần "CONDITIONAL EXPRESSIONS") điều duy nhất được kiểm tra với cách tiếp cận của bạn là đối số không trống (hoặc không xác định), trong trường hợp này, một phép đo (cho phép $DIR = '' và $FILE = '', thì đối số vẫn là '//'). - PointedEars
Bằng chứng: ls /foo, kết quả ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, kết quả 42. GNU bash, phiên bản 4.2.37 (1) -release (i486-pc-linux-gnu). - PointedEars
@PointedEars: Tôi không thể chỉ định -f tùy chọn, tại thời điểm này tôi đã viết câu trả lời này. Rõ ràng bạn luôn có thể sử dụng -e, nếu bạn không chắc chắn nó sẽ là một tập tin bình thường. Ngoài ra Trong tất cả các kịch bản của tôi, tôi trích dẫn các cấu trúc này, tôi phải vừa gửi nó mà không cần phải kiểm tra đầy đủ. - TechZilla
ACK. Nhưng bạn có thể biết rằng một lớp lót không thể giải quyết được vấn đề if-else: [ $condition ] && if_true || if_false dễ xảy ra lỗi. Trong mọi trường hợp, tôi tìm thấy [ ! -f "$file" ] && if_not_exists dễ đọc và dễ hiểu hơn [ -f "$file" ] || if_not_exists. - PointedEars


Để kiểm tra sự tồn tại của tập tin, tham số có thể là một trong những điều sau:

-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

Tất cả các bài kiểm tra dưới đây áp dụng cho các tệp, thư mục và liên kết tượng trưng thông thường:

-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

Ví dụ:

#!/bin/bash
FILE=$1

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

36
2017-12-19 12:47





Bạn nên cẩn thận khi chạy test cho một biến không được kiểm duyệt, bởi vì nó có thể tạo ra các kết quả không mong muốn:

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

Đề xuất thường là có biến thử nghiệm được bao quanh bởi dấu ngoặc kép:

#!/bin/sh
FILE=$1

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

28
2018-01-24 19:43



Đề nghị là phải có mỗi biến được bao quanh bởi dấu ngoặc kép, trừ khi bạn biết chính xác rằng bạn có một trong những trường hợp hiếm hoi mà nó không cần thiết, hoặc một trong những trường hợp hiếm hơn khi nó có hại. (Và không, đây không phải là một trong số họ.) - Uwe
Bạn có quan tâm để giải thích tại sao đây không phải là trường hợp sử dụng dấu ngoặc kép? Nếu không, tôi không thấy tính hữu ích trong nhận xét. - artdanil
Ý tôi là: Đây không phải là một trong những trường hợp hiếm hoi mà nó không cần thiết hoặc có hại. Một lập trình viên vỏ nên được sử dụng để bao bọc (gần như) mọi biến trong ngoặc kép; quy tắc này không giới hạn đối với [ ... ]. - Uwe
Xem thêm stackoverflow.com/questions/10067266/… - tripleee


Có ba cách khác nhau để thực hiện việc này:

  1. Bỏ qua trạng thái thoát bằng bash (không có câu trả lời nào khác đã nói điều này):

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

    Hoặc là:

    ! [ -e "$file" ] && echo "file does not exist"
    
  2. Phủ nhận thử nghiệm bên trong lệnh kiểm tra [ (đó là cách mà hầu hết các câu trả lời trước đây đã trình bày):

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

    Hoặc là:

    [ ! -e "$file" ] && echo "file does not exist"
    
  3. Hành động về kết quả thử nghiệm là âm tính (|| thay vì &&):

    Chỉ có:

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

    Điều này có vẻ ngớ ngẩn (IMO), không sử dụng nó trừ khi mã của bạn phải được di chuyển đến vỏ Bourne (như /bin/sh của Solaris 10 hoặc cũ hơn) thiếu toán tử phủ định đường ống (!):

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

18
2017-08-05 18:10



Bất kỳ sự khác biệt về tính di động nào giữa ! [ và [ !? - Frozen Flame
Các ! [  là POSIX cho đường ống vỏ 2.9.2 (bất kỳ lệnh nào)  Otherwise, the exit status shall be the logical NOT of the exit status of the last command và [ !   là POSIX để kiểm tra  ! expression True if expression is false. False if expression is true. Vì vậy, cả hai đều là POSIX và theo kinh nghiệm của tôi, cả hai đều được hỗ trợ rộng rãi.
@ FrozenFlame, vỏ Bourne không có ! từ khóa được giới thiệu bởi trình bao Korn. Ngoại trừ có lẽ cho Solaris 10 tuổi trở lên, bạn sẽ không gặp phải một chiếc vỏ Bourne trong những ngày này. - Stephane Chazelas


Bạn có thể làm được việc này:

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

hoặc là

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

Nếu bạn muốn kiểm tra tệp và thư mục, hãy sử dụng -etùy chọn thay vì -f. -e trả về true cho các tệp thông thường, thư mục, ổ cắm, các tệp đặc biệt của ký tự, chặn các tệp đặc biệt, v.v.


14
2018-04-28 19:40



Đó không phải là bash-cụ thể. Cú pháp xuất phát từ trình bao Korn và cũng có sẵn trong zsh và bash. Nó có lợi thế hạn chế so với tiêu chuẩn [ tiện ích ở đây mặc dù. - Stephane Chazelas
đều đặn tệp (như được kiểm tra bởi -f) và thư mục chỉ là hai trong số nhiều loại khác nhau của các tập tin. Ngoài ra còn có ổ cắm, symlink, thiết bị, fifos, cửa ra vào ... [ -e sẽ kiểm tra sự tồn tại của tệp (thuộc bất kỳ loại nào kể cả thư mục thông thường, thư mục nămo, ...) sau khi độ phân giải liên kết. - Stephane Chazelas
@StephaneChazelas: Tôi không thấy bất kỳ thẻ ksh hoặc zsh nào ở bất kỳ đâu. Chúng ta đang nói về bash hoặc cái gì đó được tiêu chuẩn hóa trên hầu hết các hệ thống Unix. Vì vậy, trong bối cảnh, đó là bash cụ thể. OP không cần phải biết mọi vỏ đạn đều có ở đó: D - Jahid
Đó là một nhận xét về "Bash cụ thể" một phần câu trả lời của bạn. - Stephane Chazelas
@StephaneChazelas: Có, và trong bối cảnh (bash và sh tiêu chuẩn), đó là bash cụ thể. Tôi không thấy điểm trong bình luận thứ hai của bạn, nó hoàn toàn nằm ngoài ngữ cảnh. OP không cần -e, anh ta cần -f. - Jahid


Trong

[ -f "$file" ]

các [ lệnh nào stat() (không phải lstat()) gọi hệ thống trên đường dẫn được lưu trong $file và trả về thật nếu hệ thống đó gọi thành công và loại tệp được trả về bởi stat() là "thông thường".

Do đó, nếu [ -f "$file" ] trả về true, bạn có thể cho biết tệp tồn tại và là một tệp thông thường hoặc một liên kết tượng trưng cuối cùng sẽ giải quyết thành một tệp thông thường (hoặc ít nhất nó là vào thời điểm của tệp stat()).

Tuy nhiên, nếu nó trả về sai (hoặc nếu [ ! -f "$file" ] hoặc là ! [ -f "$file" ] trả về true), có nhiều khả năng khác nhau:

  • tệp không tồn tại
  • tệp tồn tại nhưng không phải là tệp thông thường
  • tệp tồn tại nhưng bạn không có quyền tìm kiếm đối với thư mục chính
  • tệp tồn tại nhưng đường dẫn truy cập đó quá dài
  • tệp là một liên kết tượng trưng đến một tệp thông thường, nhưng bạn không có quyền tìm kiếm đối với một số thư mục có liên quan đến độ phân giải của liên kết tượng trưng.
  • ... bất kỳ lý do nào khác tại sao stat() cuộc gọi hệ thống có thể không thành công.

Tóm lại, nó phải là:

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

Để biết chắc chắn rằng tệp không tồn tại, chúng tôi cần stat() gọi lại hệ thống với mã lỗi của ENOENT (ENOTDIR cho chúng ta biết một trong các thành phần đường dẫn không phải là một thư mục là một trường hợp khác mà chúng ta có thể nói tệp không tồn tại theo đường dẫn đó). Thật không may [ lệnh không cho chúng tôi biết điều đó. Nó sẽ trả về false cho dù mã lỗi là ENOENT, EACCESS (permission denied), ENAMETOOLONG hay bất cứ thứ gì khác.

Các [ -e "$file" ] kiểm tra cũng có thể được thực hiện với ls -Ld -- "$file" > /dev/null. Trong trường hợp đó, ls sẽ cho bạn biết lý do tại sao stat() thất bại, mặc dù thông tin không thể dễ dàng được sử dụng theo chương trình:

$ 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

Ít nhất ls nói với tôi nó không phải vì tập tin không tồn tại mà nó không thành công. Đó là bởi vì nó không thể nói cho dù tập tin tồn tại hay không. Các [ lệnh vừa bỏ qua vấn đề.

Với zsh shell, bạn có thể truy vấn mã lỗi bằng $ERRNO biến đặc biệt sau khi thất bại [ lệnh và giải mã số đó bằng cách sử dụng $errnos mảng đặc biệt trong 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;;
    (*) echo "can't tell"; syserror "$err"
  esac
fi

(hãy cẩn thận $errnos hỗ trợ bị hỏng với một số phiên bản zsh khi được xây dựng với các phiên bản gần đây của gcc).


13
2017-10-14 15:18



Giải thích tuyệt vời! Một câu hỏi phụ: đây có phải là Stephane Chazelas, người đã phát hiện ra cú sốc của vỏ không? :) - Ali Haider
Vâng, đúng vậy. (Tôi không thể nói cho Stephane, nhưng có những mục khác trong vũ trụ Stack Exchange, nơi tôi đã nhìn thấy nó thảo luận, và có, đây là một.) - Mike S