Bagaimana saya tahu jika file biasa tidak ada di Bash?


3266

Saya telah menggunakan skrip berikut untuk melihat apakah ada file:

#!/bin/bash

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

Apa sintaks yang benar untuk digunakan jika saya hanya ingin memeriksa apakah file tersebut tidak ada?

#!/bin/bash

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

187

9
Menjadi orang yang sangat malas seperti saya, saya biasanya akan menggunakan konstruksi solusi konyol berikut: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Mungkin lebih baik saya menemukan pertanyaan ini dan belajar untuk melakukannya dengan cara yang lebih tepat. :)
Alderath

5
Untuk menjadi megah, Anda harus mengatakan "file biasa", karena sebagian besar dokumen UNIX / POSIX merujuk secara umum ke semua jenis entri sistem file, "file" sederhana, misalnya, tautan simbolik adalah jenis file, seperti pipa bernama , file biasa, direktori, blok spesial, spesial karakter, socket, dll.
kevinarpe

11
@ kevinarpe jika Anda ingin menguji apakah ada sesuatu , gunakan -e. -f tidak akan mengambil direktori, symlink, dll.
Benubird

14
Agar aman, selalu gunakan tanda kutip ganda untuk menangani nama file dengan benar dengan spasi, misalnya, FILE=$1-> FILE="$1"dan if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Jawaban:


4526

The uji perintah ( [di sini) memiliki "tidak" operator logis yang merupakan tanda seru (mirip dengan banyak bahasa lain). Coba ini:

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

208
Lebih ringkas: [! -f /tmp/foo.txt] && echo "File tidak ditemukan!"
DavidWinterbottom

38
Saya berjuang sedikit untuk menemukan sintaks yang tepat untuk "jika ada 2 file tidak ada". Berikut ini berfungsi:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk

153
@ DavidvidWinterbottom Bahkan lebih lezat:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.

27
Parameter dapat berupa salah satu dari yang berikut:-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
Ada asimetri dalam penggunaan ! -fdengan &&versus penggunaan -fdengan ||. Ini ada hubungannya dengan kode keluar yang dikembalikan oleh pemeriksaan tidak ada. Jika Anda membutuhkan baris Anda untuk selalu keluar dengan bersih dengan kode keluar 0 (dan kadang-kadang Anda tidak ingin kendala ini), kedua pendekatan tidak dapat dipertukarkan. Atau, cukup gunakan ifpernyataan dan Anda tidak perlu lagi khawatir tentang kode keluar dari pemeriksaan non-keberadaan Anda.
Acumenus

670

Pengujian File Bash

-b filename- Blok file khusus
-c filename- File karakter khusus
-d directoryname- Periksa keberadaan direktori
-e filename- Periksa keberadaan file, terlepas dari jenis (node, direktori, socket, dll.)
-f filename- Periksa keberadaan file biasa bukan direktori
-G filename- Periksa apakah file ada dan dimiliki oleh ID grup yang efektif
-G filename set-group-id- Benar jika file ada dan diatur-grup-id
-k filename- Sticky bit
-L filename- Tautan simbolik
-O filename- Benar jika file ada dan dimiliki oleh id pengguna yang efektif
-r filename- Periksa apakah file dapat dibaca
-S filename- Periksa apakah file soket
-s filename- Periksa apakah file adalah soket - Periksa apakah file bukan nol ukuran
-u filename- Periksa apakah file set-user-id bit diatur
-w filename- Periksa apakah file dapat ditulisi
-x filename- Periksa apakah file dapat dieksekusi

Cara Penggunaan:

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

Sebuah ekspresi tes dapat dinegasikan dengan menggunakan !operator yang

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

1
@ 0x90 Jika Anda mau, Anda bebas mengedit posting saya dan menambahkannya ke daftar. Saya kira maksud Anda: -n String- Periksa apakah panjang string tidak nol. Atau maksud Anda file1 -nt file2- Periksa apakah file1 lebih baru dari file 2 (Anda juga dapat menggunakan -ot untuk yang lebih tua)
BlueCacti

1
Tentang -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
mengapa beberapa tidak menambahkan fungsi seperti fungsi ada () {⏎ jika [-e "$ 1"]; lalu gema "$ 1 ada" gema lain "$ 1 tidak ada" fi}
Mz A

291

Anda dapat meniadakan ekspresi dengan "!":

#!/bin/bash
FILE=$1

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

Halaman manual yang relevan adalah man testatau, setara, man [- atau help testatau help [untuk perintah bash bawaan.


4
Dalam bash , [adalah builtin. Jadi informasi yang relevan agak diperoleh oleh help [... tetapi ini menunjukkan bahwa [ini adalah sinonim untuk testbuiltin, maka informasi yang relevan agak diperoleh oleh help test. Lihat juga bagian Ekspresi Bersyarat Bash dalam manual .
gniourf_gniourf

@ gniourf_gniourf: Ya, tetapi perintah built-in bash [berperilaku sangat mirip dengan [perintah eksternal , jadi salah satu man testatau man [akan memberi Anda ide bagus tentang cara kerjanya.
Keith Thompson

8
@KeithThompson kecuali bahwa bash builtin [memiliki lebih banyak sakelar daripada perintah eksternal yang [ditemukan pada sistem saya ... Secara umum saya percaya lebih baik membaca dokumentasi khusus untuk alat yang diberikan, dan bukan dokumentasi yang spesifik untuk yang terkait dengan samar lainnya. Tapi saya mungkin salah;)
gniourf_gniourf

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

Juga, mungkin saja file tersebut merupakan tautan simbolik yang rusak, atau file tidak biasa, seperti misalnya soket, perangkat, atau fifo. Misalnya, untuk menambahkan tanda centang untuk symlink yang rusak:

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
Bolehkah saya bertanya mengapa kedua "[" dalam ujian? (mis. [[! -a $ FILE]]). Saya mencoba semua opsi yang disebutkan pada kotak solaris dan hanya itu yang berhasil, sangat berterima kasih, tetapi mengapa?
Dimitrios Mistriotis

30
Kurung ganda adalah ekstensi "modern"; misalnya mereka tidak akan melakukan pemisahan kata (seperti untuk nama file dengan spasi) dan masih berfungsi untuk string kosong: mywiki.wooledge.org/BashFAQ/031
bw1024

7
menurut tldp.org/LDP/abs/html/fto.html -a sama artinya dengan -e. Itu telah "ditinggalkan," dan penggunaannya tidak disarankan. Bagaimanapun +1 untuk menyebutkan untuk memeriksa symlink rusak juga
Luca Borrione

4
@dimitrismistriotis two "[" adalah ekstensi non-portabel yang diimplementasikan (berbeda) oleh zsh & bash; umumnya Anda harus menghindarinya jika memungkinkan.
Orang Baik

7
Saya memilih ini karena ini digunakan [[. Ini adalah ekstensi yang banyak digunakan. Jika Anda tahu Anda menggunakan bash maka tidak ada alasan untuk tidak menggunakannya. Ini jauh lebih sedikit kesalahan daripada [.
Michael Potter

101

Perlu disebutkan bahwa jika Anda perlu menjalankan satu perintah, Anda dapat menyingkat

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

untuk

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

atau

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

Terima kasih! Membutuhkan alternatif yang tidak menggunakan [[]]
Jonathan

69

Saya lebih suka melakukan one-liner berikut, dalam format yang kompatibel dengan POSIX shell:

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

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

Untuk beberapa perintah, seperti yang akan saya lakukan dalam skrip:

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

Setelah saya mulai melakukan ini, saya jarang menggunakan sintaks yang diketik sepenuhnya !!


1
Pertama-tama, referensi variabel yang tidak dikutip adalah rawan kesalahan. Yang mengatakan, di mana isinya di setiap pesta manualnya bahwa [atau testbuilt-in akan menguji keberadaan file dari argumen secara default (sebagai lawan -e)? Apakah itu tidak ambigu? AFAIK (dan AIUI bagian "EKSPRESI KONDISI") satu-satunya hal yang diuji dengan pendekatan Anda adalah bahwa argumennya tidak kosong (atau tidak terdefinisi), yang dalam hal ini adalah tautologi (biarkan $DIR = ''dan $FILE = '', maka argumennya masih '//').
PointedEars

1
Bukti:, ls /foohasil ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, hasil 42. GNU bash, versi 4.2.37 (1) -release (i486-pc-linux-gnu).
PointedEars

@PointedEars: Saya gagal menentukan -fopsi, saat ini saya menulis jawaban ini. Tentunya Anda selalu bisa menggunakan -e, jika Anda tidak yakin itu akan menjadi file biasa. Selain itu Dalam semua skrip saya kutip konstruk ini, saya pasti baru saja mengirimkan ini tanpa bukti yang memadai.
JM Becker

ACK. Tapi Anda mungkin tahu bahwa one-liner tidak bisa menyelesaikan masalah if-else: [ $condition ] && if_true || if_falserawan kesalahan. Bagaimanapun, saya menemukan [ ! -f "$file" ] && if_not_existslebih mudah untuk membaca dan memahami daripada [ -f "$file" ] || if_not_exists.
PointedEars

55

Untuk menguji keberadaan file, parameter dapat berupa salah satu dari yang berikut:

-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

Semua tes di bawah ini berlaku untuk file biasa, direktori, dan 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

Skrip contoh:

#!/bin/bash
FILE=$1

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

39

Kamu bisa melakukan ini:

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

atau

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

Jika Anda ingin memeriksa file dan folder keduanya, gunakan -eopsi alih-alih -f. -emengembalikan true untuk file biasa, direktori, socket, file karakter khusus, blok file khusus dll.


1
Itu tidak spesifik untuk bash. Sintaksnya berasal dari shell Korn dan juga tersedia dalam zsh dan bash. Ini memiliki keunggulan terbatas atas [utilitas standar di sini.
Stephane Chazelas

file biasa (seperti yang diperiksa -f) dan direktori hanyalah dua dari banyak jenis file . Ada juga soket, symlink, perangkat, fifos, pintu ... [ -eakan menguji keberadaan file (jenis apa pun termasuk reguler, fifo, direktori ...) setelah resolusi symlink .
Stephane Chazelas

@StephaneChazelas: Saya tidak melihat tag ksh atau zsh di mana pun. Kita berbicara tentang bash atau sesuatu yang distandarisasi pada kebanyakan sistem unix. Jadi, dalam konteksnya, ini khusus untuk bash. OP tidak perlu mengetahui setiap kerang apa pun yang ada di luar sana: D
Jahid

Itu adalah komentar pada bagian "spesifik Bash" dari jawaban Anda.
Stephane Chazelas

@StephaneChazelas: Ya, dan dalam konteks (bash dan sh standar), ini spesifik bash. Saya tidak mengerti maksud dalam komentar kedua Anda, itu sepenuhnya di luar konteks. OP tidak perlu -e, dia perlu -f.
Jahid

35

Anda harus berhati-hati menjalankan testvariabel yang tidak dikutip, karena mungkin menghasilkan hasil yang tidak diharapkan:

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

Rekomendasi biasanya memiliki variabel yang diuji dikelilingi oleh tanda kutip ganda:

#!/bin/sh
FILE=$1

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

8
Rekomendasi adalah untuk membuat setiap variabel dikelilingi oleh tanda kutip ganda, kecuali jika Anda tahu persis bahwa Anda memiliki salah satu kasus langka di mana itu tidak perlu, atau salah satu kasus lebih jarang di mana itu berbahaya. (Dan tidak, ini bukan salah satunya.)
Uwe

Apakah Anda ingin menjelaskan mengapa ini bukan kasus untuk menggunakan tanda kutip ganda? Kalau tidak, saya tidak melihat kegunaan dalam komentar.
artdanil

4
Maksud saya: Ini bukan salah satu kasus langka yang tidak perlu atau berbahaya. Seorang programmer shell harus terbiasa melampirkan (hampir) setiap variabel dalam tanda kutip ganda; aturan ini tidak terbatas pada [ ... ].
Uwe


24

Ada tiga cara berbeda untuk melakukan ini:

  1. Meniadakan status keluar dengan bash (tidak ada jawaban lain yang mengatakan ini):

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

    Atau:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Meniadakan tes di dalam perintah tes [(itu adalah cara sebagian besar jawaban sebelum disajikan):

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

    Atau:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Bertindak atas hasil tes menjadi negatif ( ||bukan &&):

    Hanya:

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

    Ini terlihat konyol (IMO), jangan gunakan kecuali kode Anda harus portabel ke shell Bourne (seperti /bin/shSolaris 10 atau sebelumnya) yang tidak memiliki operator negasi pipa ( !):

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

Adakah perbedaan portabilitas antara ! [dan [ !?
Api Beku

3
Ini ! [ adalah POSIX untuk pipeline 2.9.2 (perintah apa saja) Otherwise, the exit status shall be the logical NOT of the exit status of the last command dan [ ! POSIX untuk pengujian ! expression True if expression is false. False if expression is true. Jadi, keduanya POSIX, dan dalam pengalaman saya, keduanya didukung secara luas.

1
@ FrostFlame, shell Bourne tidak memiliki !kata kunci yang diperkenalkan oleh shell Korn. Kecuali mungkin untuk Solaris 10 dan lebih tua, Anda tidak mungkin menemukan shell Bourne hari ini.
Stephane Chazelas

23

Di

[ -f "$file" ]

yang [perintah melakukan stat()(tidak lstat()) system call pada jalur yang disimpan dalam $filedan mengembalikan benar jika sistem panggilan berhasil dan jenis file sebagai dikembalikan oleh stat()adalah " biasa ".

Jadi, jika [ -f "$file" ]mengembalikan true, Anda dapat mengetahui bahwa file tersebut memang ada dan merupakan file biasa atau symlink yang akhirnya diputuskan menjadi file biasa (atau setidaknya itu pada saat filestat() ).

Namun jika itu mengembalikan false (atau jika [ ! -f "$file" ]atau ! [ -f "$file" ]mengembalikan true), ada banyak kemungkinan berbeda:

  • file tidak ada
  • file ada tetapi tidak biasa file (bisa berupa perangkat, fifo, direktori, socket ...)
  • file ada tetapi Anda tidak memiliki izin pencarian ke direktori induk
  • file ada tetapi jalur untuk mengaksesnya terlalu panjang
  • file tersebut adalah symlink ke file biasa, tetapi Anda tidak memiliki izin pencarian untuk beberapa direktori yang terlibat dalam resolusi symlink.
  • ... alasan lain mengapa stat()panggilan sistem mungkin gagal.

Singkatnya, seharusnya:

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

Untuk mengetahui dengan pasti bahwa file tersebut tidak ada, kita perlu stat()panggilan sistem untuk kembali dengan kode kesalahan ENOENT( ENOTDIRmemberitahu kita salah satu komponen jalur bukan direktori adalah kasus lain di mana kita dapat memberi tahu file tidak ada di jalur itu). Sayangnya [perintah itu tidak memberi tahu kami. Ini akan mengembalikan false apakah kode kesalahan ENOENT, EACCESS (izin ditolak), ENAMETOOLONG atau yang lainnya.

The [ -e "$file" ]tes juga dapat dilakukan dengan ls -Ld -- "$file" > /dev/null. Dalam hal ini, lsakan memberi tahu Anda mengapa stat()gagal, meskipun informasi tidak dapat dengan mudah digunakan secara terprogram:

$ 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

Setidaknya lsmemberitahu saya itu bukan karena file itu tidak ada yang gagal. Itu karena tidak tahu apakah file itu ada atau tidak. The [perintah hanya mengabaikan masalah.

Dengan zshshell, Anda dapat meminta kode kesalahan dengan $ERRNOvariabel khusus setelah [perintah gagal , dan mendekode angka itu menggunakan $errnosarray khusus dalam zsh/systemmodul:

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

(berhati-hatilah $errnosdukungannya rusak dengan beberapa versi zshketika dibangun dengan versi terbarugcc ).


11

Untuk membalik tes, gunakan "!". Itu sama dengan operator logika "bukan" dalam bahasa lain. Coba ini:

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

Atau ditulis dengan cara yang sedikit berbeda:

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

Atau Anda bisa menggunakan:

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

Atau, satukan semuanya:

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

Yang dapat ditulis (menggunakan operator "dan" saat itu: &&) sebagai:

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

Yang terlihat lebih pendek seperti ini:

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


10

Kode ini juga berfungsi.

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

7

Cara paling sederhana

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

6

Script shell ini juga berfungsi untuk menemukan file di direktori:

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
Tidak jelas apakah ini menambahkan sesuatu yang belum diberikan jawaban lain. Sulit mengkodekan nama path. Karena pertanyaan ini ditandai dengan bash , maka bisa digunakan read -p "Enter file name: " -r auntuk meminta dan membaca. Itu menggunakan tanda kutip di sekitar variabel; itu bagus, tetapi harus dijelaskan. Mungkin lebih baik jika itu menggema nama file. Dan ini memeriksa apakah file itu ada dan tidak kosong (itulah artinya -s) sedangkan pertanyaannya menanyakan file apa pun, kosong atau tidak (yang -flebih sesuai).
Jonathan Leffler

4

terkadang mungkin berguna untuk menggunakan && dan || operator.

Seperti di (jika Anda memiliki perintah "test"):

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

atau

test -b $FILE || echo File there!

2

Jika Anda ingin menggunakan testalih-alih [], maka Anda dapat menggunakan !untuk mendapatkan negasi:

if ! test "$FILE"; then
  echo "does not exist"
fi

0

Anda juga dapat mengelompokkan beberapa perintah dalam satu liner

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

atau

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

Jika nama file tidak keluar, hasilnya akan menjadi

test1
test2
test3

Catatan: (...) berjalan dalam subkulit, {...;} berjalan dalam shell yang sama. Notasi braket keriting hanya bekerja di bash.


1
Tidak, notasi braket keriting bukan hanya bash; ini bekerja di semua shell yang sesuai dengan POSIX.
Charles Duffy

0
envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi

Harap berikan penjelasan beserta kodenya.
zohar

Meskipun kode ini dapat menyelesaikan masalah OP, yang terbaik adalah memasukkan penjelasan tentang bagaimana kode Anda mengatasi masalah OP. Dengan cara ini, pengunjung masa depan dapat belajar dari posting Anda, dan menerapkannya pada kode mereka sendiri. SO bukan layanan pengkodean, tetapi sumber daya untuk pengetahuan. Juga, kualitas tinggi, jawaban lengkap lebih cenderung terunggulkan. Fitur-fitur ini, bersama dengan persyaratan bahwa semua posting mandiri, adalah beberapa kekuatan SO sebagai platform, yang membedakannya dari forum. Anda dapat mengedit untuk menambahkan info tambahan & / atau untuk melengkapi penjelasan Anda dengan dokumentasi sumber.
ysf
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.