ตรวจสอบว่าผ่านการโต้แย้งเป็นไฟล์หรือไดเรกทอรีใน Bash


156

ฉันพยายามเขียนสคริปต์อย่างง่าย ๆ ใน Ubuntu ซึ่งจะทำให้ฉันสามารถส่งผ่านชื่อไฟล์หรือไดเรกทอรีและสามารถทำสิ่งที่เฉพาะเจาะจงเมื่อเป็นไฟล์และอย่างอื่นเมื่อเป็นไดเรกทอรี ปัญหาที่ฉันมีคือเมื่อชื่อไดเรกทอรีหรือไฟล์อาจมีช่องว่างหรือตัวอักษรที่สามารถหลีกเลี่ยงได้อยู่ในชื่อ

นี่คือรหัสพื้นฐานของฉันด้านล่างและการทดสอบสองสามข้อ

#!/bin/bash

PASSED=$1

if [ -d "${PASSED}" ] ; then
    echo "$PASSED is a directory";
else
    if [ -f "${PASSED}" ]; then
        echo "${PASSED} is a file";
    else
        echo "${PASSED} is not valid";
        exit 1
    fi
fi

และนี่คือผลลัพธ์:

andy@server~ $ ./scripts/testmove.sh /home/andy/
/home/andy/ is a directory

andy@server~ $ ./scripts/testmove.sh /home/andy/blah.txt
/home/andy/blah.txt is a file

andy@server~ $ ./scripts/testmove.sh /home/andy/blah\ with\ a\ space.txt
/home/andy/blah with a space.txt is not valid

andy@server~ $ ./scripts/testmove.sh /home/andy\ with\ a\ space/
/home/andy with a space/ is not valid

เส้นทางเหล่านั้นทั้งหมดถูกต้องและมีอยู่จริง


5
if- elseโครงสร้างใน Bash รองรับelifเช่นกัน เพียงแค่ FYI

3
@glenn - อยากรู้อยากเห็นคำพูดไม่จำเป็นต้องใช้ในการกำหนดตัวแปร
John Kugelman

เป็นไปได้ที่ซ้ำกันของตรวจสอบว่ามีไดเรกทอรีอยู่ในเชลล์สคริปต์หรือไม่
jww

คำตอบ:


211

ว่าควรจะทำงาน ฉันไม่แน่ใจว่าทำไมมันล้มเหลว คุณกำลังพูดถึงตัวแปรของคุณอย่างถูกต้อง จะเกิดอะไรขึ้นถ้าคุณใช้สคริปต์นี้กับคู่[[ ]]?

if [[ -d $PASSED ]]; then
    echo "$PASSED is a directory"
elif [[ -f $PASSED ]]; then
    echo "$PASSED is a file"
else
    echo "$PASSED is not valid"
    exit 1
fi

[ ]วงเล็บคู่เป็นส่วนขยายของการทุบตี ไม่ต้องการให้มีการอ้างถึงตัวแปรแม้ว่าจะมีช่องว่างก็ตาม

ควรลองด้วยเช่นกัน: -eเพื่อทดสอบว่าเส้นทางนั้นมีอยู่หรือไม่โดยไม่ต้องทดสอบว่าเป็นไฟล์ประเภทใด


9

อย่างน้อยเขียนโค้ดโดยไม่ต้องมีต้นไม้เป็นพวง:

#!/bin/bash

PASSED=$1

if   [ -d "${PASSED}" ]
then echo "${PASSED} is a directory";
elif [ -f "${PASSED}" ]
then echo "${PASSED} is a file";
else echo "${PASSED} is not valid";
     exit 1
fi

เมื่อฉันใส่มันลงในไฟล์ "xx.sh" และสร้างไฟล์ "xx sh" และเรียกใช้ฉันจะได้รับ:

$ cp /dev/null "xx sh"
$ for file in . xx*; do sh "$file"; done
. is a directory
xx sh is a file
xx.sh is a file
$

เนื่องจากคุณมีปัญหาคุณควรแก้ไขข้อบกพร่องของสคริปต์โดยเพิ่ม:

ls -l "${PASSED}"

สิ่งนี้จะแสดงให้คุณเห็นสิ่งที่lsคิดเกี่ยวกับชื่อที่คุณผ่านสคริปต์


4

การใช้คำสั่ง "file" อาจมีประโยชน์สำหรับสิ่งนี้:

#!/bin/bash
check_file(){

if [ -z "${1}" ] ;then
 echo "Please input something"
 return;
fi

f="${1}"
result="$(file $f)"
if [[ $result == *"cannot open"* ]] ;then
        echo "NO FILE FOUND ($result) ";
elif [[ $result == *"directory"* ]] ;then
        echo "DIRECTORY FOUND ($result) ";
else
        echo "FILE FOUND ($result) ";
fi

}

check_file "${1}"

ตัวอย่างผลลัพธ์:

$ ./f.bash login
DIRECTORY FOUND (login: directory) 
$ ./f.bash ldasdas
NO FILE FOUND (ldasdas: cannot open `ldasdas' (No such file or  directory)) 
$ ./f.bash evil.php 
FILE FOUND (evil.php: PHP script, ASCII text) 

FYI: คำตอบข้างต้นใช้ได้ผล แต่คุณสามารถใช้ -s เพื่อช่วยในสถานการณ์แปลก ๆ โดยตรวจสอบไฟล์ที่ถูกต้องก่อน:

#!/bin/bash

check_file(){
    local file="${1}"
    [[ -s "${file}" ]] || { echo "is not valid"; return; } 
    [[ -d "${file}" ]] && { echo "is a directory"; return; }
    [[ -f "${file}" ]] && { echo "is a file"; return; }
}

check_file ${1}

ดังนั้นไฟล์เปล่าไม่ถูกต้อง (เพราะ-sตรวจสอบไฟล์ที่ไม่ว่างเปล่าไฟล์หนึ่งที่มีขนาดไม่เป็นศูนย์)? และคุณจะไม่พิมพ์การวินิจฉัยใด ๆ สำหรับบล็อกพิเศษอักขระพิเศษ FIFO ฯลฯ Symlinks อาจแก้ไขสิ่งที่อยู่ปลายสุดของลิงก์ symlink ที่ใช้งานไม่ได้มีปัญหามากกว่า
Jonathan Leffler

สิ่งใดที่คุณแนะนำเป็นการแก้ไขฉันไม่ได้ติดตามความเห็นของคุณ
Mike Q

ใช้ธง--brief fileมันเพียงแค่ส่งออกdirectoryเมื่อมันเป็น
Berkant Ipek

2

การใช้-fและ-dเปิดสวิตช์/bin/test:

F_NAME="${1}"

if test -f "${F_NAME}"
then                                   
   echo "${F_NAME} is a file"
elif test -d "${F_NAME}"
then
   echo "${F_NAME} is a directory"
else                                   
   echo "${F_NAME} is not valid"
fi

โดยทั่วไปคุณไม่ควรกังวลที่จะเพิ่มคำตอบใหม่ลงในคำถามเก่าเมื่อมีคำตอบที่เทียบเท่าแล้ว หากคุณมีข้อมูลใหม่ที่น่าตกใจเพื่อเพิ่ม แต่ทั้งหมดหมายถึงให้คำตอบ แต่สิ่งที่คุณพูดได้ถูกพูดไปแล้ว ( testโดยปกติแล้วจะเป็นเชลล์ในตัวแม้ว่าโดยปกติจะมีไฟล์ที่เรียกใช้งานได้เช่น/bin/testและ/bin/[)
Jonathan Leffler

1

ทางออกที่สง่างามกว่า

echo "Enter the file name"
read x
if [ -f $x ]
then
    echo "This is a regular file"
else
    echo "This is a directory"
fi

2
การพรอมต์สำหรับอินพุตแทนที่จะใช้อาร์กิวเมนต์บรรทัดคำสั่งมักจะไม่ "สวยหรู" if [ -f "$x" ]คุณควรพูดตัวแปรในการทดสอบ:
Jonathan Leffler


-1

สิ่งนี้จะทำงานได้:

#!/bin/bash

echo "Enter your Path:"
read a

if [[ -d $a ]]; then 
    echo "$a is a Dir" 
elif [[ -f $a ]]; then 
    echo "$a is the File" 
else 
    echo "Invalid path" 
fi

2
กรุณาคุณสามารถเพิ่มคำอธิบายเมื่อคุณตอบคำถามได้ไหม?
Sébastien Temprado

-2
#!/bin/bash                                                                                               
echo "Please Enter a file name :"                                                                          
read filename                                                                                             
if test -f $filename                                                                                      
then                                                                                                      
        echo "this is a file"                                                                             
else                                                                                                      
        echo "this is not a file"                                                                         
fi 

เป็นการดีที่สุดที่จะสะท้อนชื่อไฟล์ในผลลัพธ์เช่นเดียวกับรหัสในคำถาม ไม่ชัดเจนว่าการให้ชื่อไฟล์เป็นการปรับปรุง รหัสไม่ได้แยกความแตกต่างระหว่างไฟล์ไดเรกทอรีและ 'อื่น ๆ ' มันก็โอเคที่จะเพิ่มคำตอบใหม่ให้กับคำถามเก่าถ้าไม่มีคำตอบหรือคุณมีข้อมูลใหม่ที่จะบอก นั่นไม่ใช่กรณีที่นี่
Jonathan Leffler

-2

หนึ่งในสายการบิน

touch bob; test -d bob && echo 'dir' || (test -f bob && echo 'file')

ผลลัพธ์เป็นจริง (0) (dir) หรือจริง (0) (ไฟล์) หรือเท็จ (1) (ไม่ใช่)


ถ้าฉันแทนที่touch bobด้วยmkdir bobฉันจะได้ผลลัพธ์: dirและfileสองบรรทัด
Jonathan Leffler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.