file-io شرح - كيف يمكنني معرفة ما إذا كان الملف العادي غير موجود في Bash؟




معنى script (15)

لقد استخدمت البرنامج النصي التالي لمعرفة ما إذا كان الملف موجودًا:

#!/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

Answers

أبسط طريقة

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

هناك ثلاث طرق مميزة للقيام بذلك:

  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) ، لا تستخدمه ما لم يكن رمزك محمولًا إلى غلاف Bourne (مثل /bin/sh لـ Solaris 10 أو ما قبله) التي تفتقر إلى عامل إلغاء خط أنابيب ( ! ):

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

في

[ -f "$file" ]

[ الأمر هو stat() (غير lstat() ) استدعاء النظام على المسار المخزن في $file وإرجاع true إذا نجح ذلك استدعاء النظام ونوع الملف كما تم إرجاعها بواسطة stat() هو "عادي".

لذا إذا عاد [ -f "$file" ] إلى true ، فيمكنك أن تقول أن الملف موجود وأنه عبارة عن ملف عادي أو رابط يرمز إليه في النهاية إلى ملف عادي (أو على الأقل كان في وقت إصدار stat() ).

ولكن إذا كانت ترجع false (أو إذا كان [ ! -f "$file" ] أو ! [ -f "$file" ] بإرجاع true) ، فهناك العديد من الاحتمالات المختلفة:

  • الملف غير موجود
  • الملف موجود ولكن ليس ملف عادي
  • الملف موجود ولكن ليس لديك إذن بحث إلى الدليل الأصل
  • الملف موجود ولكن هذا المسار للوصول إليه طويل جدًا
  • الملف عبارة عن رابط إلى ملف عادي ، ولكن ليس لديك إذن بحث لبعض الدلائل المتضمنة في حل الشفرة.
  • ... أي سبب آخر قد يفشل استدعاء النظام 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() مع رمز خطأ من ENOENT (يخبرنا ENOTDIR أحد مكونات المسار ليس دليلاً هو حالة أخرى يمكننا فيها معرفة الملف غير موجود في هذا المسار). لسوء الحظ ، لا يسمح لنا الأمر بمعرفة ذلك. سيعود كاذب ما إذا كان رمز الخطأ هو ENOENT أو EACCESS (تم رفض الإذن) أو ENAMETOOLONG أو أي شيء آخر.

يمكن أيضًا إجراء اختبار [ -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

يقول لي على الأقل أنه ليس بسبب عدم وجود الملف الذي يفشل. لأنه لا يمكنه معرفة ما إذا كان الملف موجودًا أم لا. الأمر [ تجاهل المشكلة فقط.

باستخدام shell zsh ، يمكنك الاستعلام عن رمز الخطأ المتغير الخاص $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;;
    (*) echo "can't tell"; syserror "$err"
  esac
fi

(حذار دعم $errnos مع بعض إصدارات zsh عند إنشاء الإصدارات الأخيرة من gcc ).


تجدر الإشارة إلى أنه إذا كنت بحاجة إلى تنفيذ أمر واحد ، فيمكنك الاختصار

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

إلى

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

أو

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


يمكنك إلغاء تعبير باستخدام "!":

#!/bin/bash
FILE=$1

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

صفحة الرجل ذات الصلة هي man test أو ، على قدم المساواة ، man [ - أو help test أو help [ لأمر باش المدمج في.


يعمل هذا البرنامج النصي 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

هذا الرمز يعمل أيضا.

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

أفضل القيام بما يلي أحادي الخطوط ، في تنسيق متوافق مع shell POSIX :

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

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

بالنسبة إلى أمرين ، مثلما يمكنني فعله في نص برمجي:

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

بمجرد أن بدأت في القيام بذلك ، نادراً ما أستخدم تركيب الجملة المكتوب بالكامل!


لاختبار وجود الملف ، يمكن أن تكون المعلمة أيًا مما يلي:

-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

يجب أن تكون حذراً بشأن تشغيل test للمتغير غير المتغير ، لأنه قد يؤدي إلى نتائج غير متوقعة:

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

عادة ما تكون التوصية هي أن يكون المتغير الذي تم اختباره محاطًا بعلامات اقتباس مزدوجة:

#!/bin/sh
FILE=$1

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

في بعض الأحيان قد يكون سهل الاستخدام & & و || العاملين.

مثل في (إذا كان لديك "اختبار"):

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

أو

test -b $FILE || echo File there!

باش اختبار الملف

-b filename - حظر ملف خاص
-c filename - ملف شخصي خاص
-d directoryname - التحقق من وجود دليل
-e filename - التحقق من وجود الملف ، بغض النظر عن النوع (عقدة ، دليل ، مقبس ، إلخ.)
-f filename - تحقق من وجود ملف عادي وليس دليلاً
-G filename - تحقق من وجود ملف ومملوكة لمجموعة فعالة من الهوية
-G filename set-group-id - صحيح إذا كان الملف موجودا وهو معرف المجموعة المحددة
-k filename - اللزجة
-L filename - ارتباط رمزي
-O filename - صحيح إذا كان الملف موجودا ومملوكة لمعرف المستخدم الفعال
-r filename - تحقق مما إذا كان الملف قابلاً للقراءة
-S filename - تحقق ما إذا كان الملف مأخذ التوصيل
-s filename - تحقق ما إذا كان الملف حجم غير صفري
-u filename - تحقق مما إذا كان قد تم تعيين ملف بت مجموعة المستخدم
-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 

انت تستطيع فعل ذالك:

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

أو

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

إذا كنت تريد التحقق من الملف والمجلد على حدٍ سواء ، فاستخدم خيار -e بدلاً من -f . -e يرجع صحيح للملفات العادية ، والأدلة ، ومآخذ التوصيل ، وملفات شخصية خاصة ، ومنع ملفات خاصة الخ


POSIX (واجهة نظام التشغيل المحمولة)

أفتقد هنا النقطة الأساسية ، وهي قابلية النقل. لهذا السبب يحتوي رأسي على POSIX في حد ذاته.

بشكل أساسي ، جميع الإجابات التي تم التصويت عليها صحيحة ، باستثناء أنها BASH - محددة أكثر من اللازم.

لذا ، أريد فقط إضافة المزيد من المعلومات حول قابلية النقل.

  1. [ و ] الأقواس كما في [ "$var" = true ] ليست ضرورية ، ويمكنك حذفها واستخدام الأمر test مباشرةً:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. تخيل ما تعنيه هذه الكلمات true false للقشرة ، اختبرها بنفسك:

    echo $((true))
    
    0
    
    echo $((false))
    
    1
    

    لكن استخدام علامات الاقتباس:

    echo $(("true"))
    
    bash: "true": syntax error: operand expected (error token is ""true"")
    sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""
    

    الشيء نفسه ينطبق على:

    echo $(("false"))
    

    لا يمكن لل shell تفسيرها بخلاف سلسلة. آمل أن تحصل على فكرة مدى جودة استخدام الكلمة الرئيسية المناسبة بدون علامات اقتباس.

    لكن لا أحد قال ذلك في إجابات سابقة.

  3. ماذا يعني هذا؟ حسنا ، عدة أشياء.

    • يجب أن تعتاد على الكلمات الأساسية المنطقية تعامل في الواقع مثل الأرقام ، وهذا true = 0 و false = 1 ، وتذكر أن جميع القيم غير الصفر تعامل false .

    • بما أنهم يعاملون مثل الأرقام ، يجب أن تعاملهم مثل ذلك ، أي إذا كنت قد حددت المتغير يقول:

      var_a=true
      echo "$var_a"
      
       true
      

      يمكنك إنشاء قيمة معاكسة لها من خلال:

      var_a=$((1 - $var_a))
      echo "$var_a"
      
      1
      

      كما ترون بنفسك ، تقوم shell بطباعة سلسلة true لأول مرة تستخدمها ، ولكن منذ ذلك الحين ، تعمل جميعها عبر الرقم 0 أو 1 ، على التوالي.

وأخيرًا ، ما يجب عليك فعله بكل هذه المعلومات

  • العادة الأولى الجيدة هي تعيين 0 بدلاً من true ؛ 1 بدلا من false .

  • ستكون العادة الثانية الجيدة هي اختبار ما إذا كان المتغير لا يساوي صفرًا:

    test "$var" -eq 0 && CodeIfTrue || CodeIfFalse
    




bash file-io scripting