จะตรวจสอบนามสกุลของชื่อไฟล์ในสคริปต์ทุบตีได้อย่างไร?


170

ฉันกำลังเขียนสคริปต์สร้างทุกคืนในทุบตี
ทุกอย่างเรียบร้อยและสวยหรูยกเว้นอุปสรรค์ตัวน้อย:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

ปัญหาของฉันคือการกำหนดนามสกุลไฟล์แล้วทำตาม ฉันรู้ว่าปัญหาอยู่ในคำสั่ง if ทดสอบไฟล์ txt

ฉันจะทราบได้อย่างไรว่าไฟล์นั้นมี. txt ต่อท้ายหรือไม่


สิ่งนี้จะแตกถ้าคุณมีไฟล์ที่มีช่องว่างในชื่อ
jfg956

นอกจากคำตอบของ Paul แล้วคุณสามารถใช้$(dirname $PATH_TO_SOMEWHERE)และ$(basename $PATH_TO_SOMEWHERE)แยกเป็นโฟลเดอร์และไดเรกทอรีและทำบางสิ่งบางอย่าง directory-ish และ file-ish
McPeppr

คำตอบ:


250

ฉันคิดว่าคุณต้องการพูดว่า "สี่ตัวอักษรสุดท้ายของ $ file เท่ากับ.txtหรือไม่" หากเป็นเช่นนั้นคุณสามารถใช้สิ่งต่อไปนี้:

if [ ${file: -4} == ".txt" ]

โปรดทราบว่าช่องว่างระหว่างfile:และ-4จำเป็นต้องมีเนื่องจากตัวปรับ ': -' หมายถึงบางสิ่งที่แตกต่าง


1
คุณสามารถเปลี่ยนชื่อ command.com เป็น command.txt บนเครื่อง windows ได้เช่นกัน
hometoast

9
หากคุณต้องการระบุความไม่เท่าเทียมกันอย่าลืมใส่วงเล็บพิเศษ: ถ้า [[$ {file: -4}! = ".txt"]]
Ram Rajamony

4
@RamRajamony ทำไมจึงจำเป็นต้องใช้ [[เมื่อทดสอบความไม่เท่าเทียมกัน?
PesaThe

ฉันต้องการชี้ให้เห็นว่าพื้นที่หลังลำไส้ใหญ่มีความสำคัญ ${var:-4}ไม่เหมือนกัน${var: -4} ; ตัวแรก (โดยไม่มีช่องว่าง) จะขยายเป็น '-4' ถ้า var ไม่ได้ตั้งค่าตัวที่สอง (พร้อมช่องว่าง) จะส่งคืนอักขระ 4 ตัวสุดท้ายของ var
pbatey

1
ใน bash สิ่งนี้จะสร้างข้อผิดพลาด "[: ==: ตัวดำเนินการที่ไม่คาดหวัง" หากคุณไม่ใส่เครื่องหมายคำพูดรอบตัวแปรแรก ดังนั้นif [ "${file: -4}" == ".txt" ]แทน
ไจล์ส B

264

ยี่ห้อ

if [ "$file" == "*.txt" ]

แบบนี้:

if [[ $file == *.txt ]]

นั่นคือวงเล็บคู่และไม่มีคำพูด

ด้านขวาของ ==ลวดลายเปลือกหอย หากคุณต้องการแสดงออกปกติใช้=~แล้ว


15
ฉันไม่รู้เกี่ยวกับสิ่งนี้ ดูเหมือนจะเป็นกรณีพิเศษที่ด้านขวาของ == หรือ! = ถูกขยายเป็นรูปแบบเชลล์ โดยส่วนตัวฉันคิดว่านี่ชัดเจนกว่าคำตอบของฉัน
Paul Stephenson

20
ฉันใหม่เพื่อทุบตีและฉันใช้เวลาสักครู่เพื่อหาวิธีใช้สิ่งนี้ในifคำสั่งแบบมีเงื่อนไขหลายข้อ ฉันแบ่งปันที่นี่ในกรณีที่ช่วยใครบางคน if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom

6
@cheflo นั้นดีสำหรับหลาย ๆ เงื่อนไขโดยทั่วไป if [[ $file =~ .*\.(csv|png) ]]ในกรณีที่เฉพาะเจาะจงนี้คุณยังสามารถใช้ มันสั้นกว่าชัดเจนขึ้นง่ายขึ้นในการเพิ่มส่วนขยายเพิ่มเติมและสามารถกำหนดค่าได้อย่างง่ายดาย (โดยใส่ "csv | png" ในตัวแปร)
jox

4
คุณสามารถใส่เครื่องหมายคำพูดคู่ล้อมรอบไฟล์ if [[ "$file" == *.txt ]]หากไฟล์นั้นมีช่องว่างในชื่อไฟล์จะต้องมีการอ้างอิงสองครั้ง
shawnhcorey

ฉันควรใช้คำตอบนี้แทนที่จะเป็นคำตอบที่ยอมรับหรือไม่
Freedo

26

คุณไม่สามารถมั่นใจได้ในระบบ Unix ว่าไฟล์. txt เป็นไฟล์ข้อความจริงๆ ทางออกที่ดีที่สุดของคุณคือการใช้ "ไฟล์" อาจลองใช้:

file -ib "$file"

จากนั้นคุณสามารถใช้รายการประเภท MIME เพื่อจับคู่หรือแยกส่วนแรกของ MIME ที่คุณได้รับสิ่งต่าง ๆ เช่น "text", "application" เป็นต้น


2
เช่นเดียวfile -i...กับการเข้ารหัส mime คุณสามารถใช้file --mime-type -b ...
วิลฟ์

24

คุณสามารถใช้คำสั่ง "ไฟล์" หากคุณต้องการหาข้อมูลเกี่ยวกับไฟล์มากกว่าที่จะใช้นามสกุล

หากคุณรู้สึกสบายใจที่จะใช้ส่วนขยายคุณสามารถใช้ grep เพื่อดูว่าตรงกับส่วนขยายหรือไม่


ใช่ฉันรู้ถึงfileคำสั่งแล้ว จริง ๆ แล้วฉันได้ลองจับคู่ตามผลลัพธ์ของคำสั่งดังกล่าว ... แต่ฉันล้มเหลวอย่างน่ากลัวที่คำสั่ง if เหล่านี้

17

คุณสามารถทำได้:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

11
case $FILE in *.txt ) ... ;; esacจะดูแข็งแกร่งและสำนวนมากขึ้น
tripleee

11

เช่นเดียวกับ 'ไฟล์' ใช้ 'mimetype -b' ที่เรียบง่ายขึ้นเล็กน้อยซึ่งจะใช้งานได้ไม่ว่าส่วนขยายของไฟล์จะเป็นอย่างไร

if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

แก้ไข: คุณอาจต้องติดตั้ง libfile-mimeinfo-perl ในระบบของคุณหาก mimetype ไม่พร้อมใช้งาน


1
คุณควรทำให้ชัดเจนว่าสคริปต์ 'mimetype' ไม่พร้อมใช้งานในทุกระบบ
gilbertpilz

เสร็จสิ้นเพิ่ม libfile-mimeinfo-perl
dargaud

5

คำตอบที่ถูกต้องเกี่ยวกับวิธีใช้ส่วนขยายที่มีอยู่ในชื่อไฟล์ใน linux คือ:

${filename##*\.} 

ตัวอย่างการพิมพ์นามสกุลไฟล์ทั้งหมดในไดเรกทอรี

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

คำตอบของคุณใช้เครื่องหมายแบคสแลชสองครั้ง แต่ตัวอย่างของคุณใช้เครื่องหมายแบ็กสแลชเดียว ตัวอย่างของคุณถูกต้องคำตอบของคุณไม่ได้
gilbertpilz

3

ฉันเขียนสคริปต์ทุบตีที่ดูประเภทของไฟล์จากนั้นคัดลอกไปยังตำแหน่งฉันใช้เพื่อดูวิดีโอที่ฉันดูทางออนไลน์จากแคช firefox ของฉัน:

#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

มันใช้ความคิดที่คล้ายกับที่แสดงไว้ที่นี่หวังว่านี่จะเป็นประโยชน์กับใครบางคน


2

ฉันเดาว่า'$PATH_TO_SOMEWHERE'เป็นสิ่งที่ชอบ'<directory>/*'เป็นสิ่งที่ต้องการ

ในกรณีนี้ฉันจะเปลี่ยนรหัสเป็น:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

หากคุณต้องการทำสิ่งที่ซับซ้อนมากขึ้นกับไดเรกทอรีและชื่อไฟล์ข้อความคุณสามารถ:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

หากคุณมีช่องว่างในชื่อไฟล์คุณสามารถ:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

นี่เป็นตัวอย่างที่ดีของวิธีการ 'วนรอบ' ในเชลล์ สงวนไว้อย่างชัดเจนforและwhileลูปเมื่อร่างกายห่วงต้องมีความซับซ้อนมากขึ้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.