การค้นหาไฟล์“ ไม่ใช่ไบนารี” ทั้งหมด


43

เป็นไปได้หรือไม่ที่จะใช้findคำสั่งเพื่อค้นหาไฟล์ "ที่ไม่ใช่ไบนารี" ทั้งหมดในไดเรกทอรี? นี่คือปัญหาที่ฉันพยายามแก้ไข

ฉันได้รับไฟล์เก็บถาวรจากผู้ใช้ windows ไฟล์เก็บถาวรนี้มีซอร์สโค้ดและไฟล์ภาพ ระบบการสร้างของเราเล่นได้ไม่ดีกับไฟล์ที่มีปลายแถวของ windows ฉันมีโปรแกรมบรรทัดคำสั่ง ( flip -u) ที่จะพลิกจบบรรทัดระหว่าง * ระวังและ windows ดังนั้นฉันต้องการทำอะไรแบบนี้

find . -type f | xargs flip -u

อย่างไรก็ตามหากคำสั่งนี้ทำงานกับไฟล์รูปภาพหรือไฟล์สื่อไบนารีอื่น ๆ คำสั่งนี้จะทำให้ไฟล์เสียหาย ฉันรู้ว่าฉันสามารถสร้างรายการนามสกุลไฟล์และตัวกรองด้วยสิ่งนั้นได้ แต่ฉันอยากมีบางอย่างที่ไม่พึ่งพาฉันเพื่อให้รายการนั้นเป็นปัจจุบัน

ดังนั้นมีวิธีการหาไฟล์ที่ไม่ใช่ไบนารีทั้งหมดในต้นไม้ไดเรกทอรีหรือไม่ หรือมีวิธีอื่นที่ฉันควรพิจารณาหรือไม่


1
คุณสามารถใช้fileยูทิลิตีที่ใดที่หนึ่งในสคริปต์ / ไปป์ไลน์ของคุณเพื่อระบุว่าไฟล์นั้นเป็นข้อมูลหรือข้อความ
lk-

1
สิ่งที่คุณหมายถึงไม่ใช่ไบนารี (ทุกสิ่งบนคอมพิวเตอร์ที่ทันสมัยเป็นไบนารี) ฉันเดาว่าคุณกำลังใช้ความแตกต่างจากระบบปฏิบัติการ C / PM เก่าที่มีข้อความและไฟล์ไบนารี ไฟล์ข้อความอาจมีความยาวเท่าใดก็ได้ แต่ต้องลงท้ายด้วย ctrl-z และไฟล์ไบนารีจะต้องมีบล็อก 512byte หลายบล็อก ถ้าเป็นเช่นนั้นคุณหมายถึงไฟล์ข้อความ (ฉันยังทราบด้วยว่าคุณเขียนเกี่ยวกับการลงท้ายบรรทัดในไฟล์ที่ไม่ใช่ไบนารีซึ่งจะแนะนำว่าเป็นไฟล์ข้อความ) ถูกต้องหรือไม่
ctrl-alt-delor

ไฟล์ทั้งหมดเป็นไบนารีมันเป็นเพียงการตีความ คุณกำลังขอวิธีการค้นหาไฟล์ข้อความ?
ctrl-alt-delor

@ Richard ฉันมาในยุคที่เราเรียกไฟล์ที่ต้องการตีความเป็นข้อความธรรมดาและไฟล์อื่น ๆ (รูปภาพ, เอกสารประมวลผลคำ, ฯลฯ ) ไบนารี ฉันรู้ว่ามันเป็นเพียงแค่ศูนย์และหนึ่งเดียวภายใต้ประทุน :)
อลันสตอร์ม

1
อาฉันเห็นสิ่งที่คุณหมายถึงคำศัพท์ของฉัน - ฉันจะใช้ไบนารี / ข้อความในอนาคตเพื่อหลีกเลี่ยงความสับสน Re: สิ่ง \ r \ n - ฉันเข้าใจว่าเป็นอักขระ ASCII สำหรับการขึ้นบรรทัดใหม่ของเครื่องพิมพ์ดีด (เลื่อนไปที่จุดเริ่มต้นของบรรทัด) และป้อนบรรทัด (เลื่อนลงหนึ่งบรรทัด) ดังนั้น \ r \ n จึงเป็นรูปแบบ "แม่นยำยิ่งขึ้น" ของสิ่งมีชีวิตทางกายภาพในโลกแห่งความเป็นจริงซึ่งเป็นจุดสิ้นสุดของตัวละครเส้น Pre OS X, Macs ใช้เพียงแค่นี้ ฉันมักจะเขียนสิ่งทั้งหมดออกเป็น "ตัวเลือกโดยพลการที่เร่งรีบที่เรายังคงจัดการกับ"
อลันสตอร์ม

คำตอบ:


20

ฉันจะใช้fileและไพพ์เอาท์พุทเป็น grep หรือ awk เพื่อค้นหาไฟล์ข้อความจากนั้นแตกไฟล์ชื่อไฟล์ของfileเอาท์พุตและไพพ์ของมันลงใน xargs

สิ่งที่ต้องการ:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

โปรดทราบว่า grep ค้นหาคำว่า 'ASCII text' แทนที่จะเป็น 'text' ใด ๆ - คุณอาจไม่ต้องการยุ่งกับเอกสาร Rich Text หรือ unicode text files เป็นต้น

คุณยังสามารถใช้find(หรืออะไรก็ได้) เพื่อสร้างรายการไฟล์เพื่อตรวจสอบด้วยfile:

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

-d'\n'อาร์กิวเมนต์ xargs ทำให้ xargs รักษาสายป้อนข้อมูลแต่ละเป็นอาร์กิวเมนต์ที่แยกต่างหากจึงอาหารสำหรับชื่อไฟล์ที่มีช่องว่างและตัวอักษรปัญหาอื่น ๆ คือมันเป็นทางเลือกให้กับxargs -0เมื่อแหล่งสัญญาณเข้าไม่ได้หรือไม่สามารถสร้างผลลัพธ์โมฆะคั่น (เช่นfind's -print0ตัวเลือก) อ้างอิงจากการเปลี่ยนแปลง xargs ได้รับ-d/ --delimiterตัวเลือกในเดือนกันยายน 2005 ดังนั้นควรอยู่ในลินุกซ์ distro ใด ๆ ที่ไม่ใช่โบราณ (ฉันไม่แน่ใจซึ่งเป็นเหตุผลที่ฉันตรวจสอบ - ฉันเพิ่งจำได้ว่ามันเป็น "นอกจากนี้" ล่าสุด)

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

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

ฉันเคยใช้วิธีการนี้มาหลายครั้งหลายครั้งในอดีตกับความสำเร็จ


1
ขอบคุณสำหรับโซลูชันนี้! ด้วยเหตุผลบางอย่างfileแสดงEnglish textมากกว่าASCII textบนระบบ Solaris ของฉันดังนั้นฉันจึงปรับเปลี่ยนส่วนนั้นตามลำดับ นอกจากนี้ผมแทนที่ด้วยเทียบเท่าawk -F: '{print $1}' cut -f1 -d:
Andrew Cheong

3
คุ้มค่าที่พูดgrep -Iกรองไบนารี
xenoterracide

การค้นหาคำtextควรเพียงพอ นอกจากนี้ยังจะรับfileรายละเอียดเหมือนASCII Java program textหรือหรือHTML document text troff or preprocessor input text
user1024

คำตอบของฉันคือการตอบสนอง / การปรับปรุงบางส่วนตามคำตอบนี้ จุดที่ดีมากเกี่ยวกับการ grepping ASCII textเพื่อหลีกเลี่ยง messing up RTF
Wildcard

1
xenoterracide: คุณช่วยชีวิตมนุษย์ฉันไว้! เพียงแค่ธง -I และ BINGO
Sergio Abreu

9

ไม่ไม่มีอะไรพิเศษเกี่ยวกับไฟล์ไบนารีหรือไฟล์ที่ไม่ใช่ไบนารี คุณสามารถใช้การวิเคราะห์พฤติกรรมเช่น 'มีเฉพาะอักขระใน 0x01–0x7F' แต่จะเรียกไฟล์ข้อความที่มีไฟล์ไบนารีที่ไม่ใช่ ASCII อักขระและไฟล์ข้อความไบนารีไฟล์ที่โชคไม่ดี

ตอนนี้เมื่อคุณไม่สนใจสิ่งนั้น ...

ไฟล์ zip

หากมาจากผู้ใช้ Windows ของคุณเป็นไฟล์ซิปรูปแบบ zip รองรับการทำเครื่องหมายไฟล์เป็นไบนารีหรือข้อความในไฟล์เก็บถาวร คุณสามารถใช้-aตัวเลือกunzip เพื่อให้ความสนใจกับเรื่องนี้และการแปลง แน่นอนดูย่อหน้าแรกสำหรับสาเหตุที่อาจไม่เป็นความคิดที่ดี (โปรแกรม zip อาจเดาผิดเมื่อสร้างไฟล์เก็บถาวร)

zipinfo จะบอกคุณว่าไฟล์ใดเป็น binary (b) หรือ text (t) ในรายการ zipfile

ไฟล์อื่น ๆ

คำสั่ง file จะดูไฟล์และพยายามระบุ โดยเฉพาะอย่างยิ่งคุณอาจพบว่า-iตัวเลือก (ประเภทเอาท์พุท MIME) มีประโยชน์ แปลงไฟล์ด้วย text type / * เท่านั้น


6

โซลูชันทั่วไปเพื่อประมวลผลไฟล์ที่ไม่ใช่ไบนารีเท่านั้นเมื่อbashใช้file -b --mime-encoding:

while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)

ฉันติดต่อผู้เขียนยูทิลิตี้ไฟล์และเขาเพิ่ม-00พารามิเตอร์ที่ดีในเวอร์ชั่น 5.26 (เปิดตัว 2016-04-16, เช่นใน Arch ปัจจุบันและ Ubuntu 16.10) ซึ่งพิมพ์file\0result\0ไฟล์หลายไฟล์พร้อมกันในวิธีนี้คุณสามารถทำได้ เช่น:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | 

( awkส่วนหนึ่งคือการกรองไฟล์ทุกไฟล์ที่ไม่ใช่แบบไบนารีไม่ใช่ORSตัวแยกผลลัพธ์)

สามารถใช้ในวงแน่นอน:

while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')

จากสิ่งนี้และก่อนหน้านี้ฉันสร้างbashสคริปต์เล็กน้อยสำหรับการกรองไฟล์ไบนารีซึ่งใช้วิธีการใหม่โดยใช้-00พารามิเตอร์ของfileในเวอร์ชันที่ใหม่กว่าและกลับไปยังวิธีก่อนหน้าในเวอร์ชันเก่า:

#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
      printf '%s\0' "$f"
  done
fi

หรือที่นี่อีก POSIX-y หนึ่ง แต่ต้องการการสนับสนุนสำหรับsort -V:

#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s\0' "$f"
  done
fi

6

คำตอบที่ยอมรับไม่พบพวกเขาทั้งหมดสำหรับฉัน นี่คือตัวอย่างการใช้ grep -Iเพื่อละเว้นไบนารีและละเว้นไฟล์ที่ซ่อนอยู่ทั้งหมด ...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

ที่นี่มีการใช้งานในการใช้งานจริง: dos2unix

https://unix.stackexchange.com/a/365679/112190


4

คำตอบของ Casนั้นดี แต่มันถือว่าเป็นชื่อไฟล์ที่มีเหตุผล โดยเฉพาะสันนิษฐานว่าชื่อไฟล์จะไม่มีบรรทัดใหม่

ไม่มีเหตุผลที่ดีที่จะตั้งสมมติฐานนี้ที่นี่เนื่องจากมันค่อนข้างง่าย (และจริง ๆ แล้วฉันคิดว่าสะอาดกว่า) ในการจัดการกับกรณีนี้อย่างถูกต้องเช่นกัน:

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

findคำสั่งเพียง แต่ทำให้การใช้งานของคุณสมบัติ POSIX ระบุ ใช้-execในการเรียกใช้คำสั่งโดยพลการทดสอบแบบบูลเป็นเรื่องง่ายและมีประสิทธิภาพ (จับชื่อไฟล์แปลกอย่างถูกต้อง) -print0และแบบพกพามากกว่า

ในความเป็นจริงทุกส่วนของคำสั่งที่ระบุไว้โดย POSIX flipยกเว้น

โปรดทราบว่าfileไม่รับประกันความถูกต้องของผลลัพธ์ที่ส่งคืน อย่างไรก็ตามในการฝึก grepping สำหรับ "ข้อความ ASCII" ในการส่งออกของมันค่อนข้างน่าเชื่อถือ

(อาจพลาดไฟล์ข้อความบางไฟล์ แต่เป็นไปได้ยากมากที่จะระบุไฟล์ไบนารีไม่ถูกต้องว่าเป็น "ข้อความ ASCII" และคลี่คลายมัน - ดังนั้นเราจึงทำผิดด้านของความระมัดระวัง)


ไฟล์ที่callsไม่ต้องมีอาร์กิวเมนต์สามารถทำได้ค่อนข้างช้าเช่นวิดีโอจะบอกทุกอย่างเกี่ยวกับการเข้ารหัส
phk

-นอกจากนี้คุณจะสมมติว่าไม่มีไฟล์เริ่มต้นด้วย
phk

และฉันไม่เห็นเหตุผลว่าทำไมคุณไม่เพียงแค่โทรออกครั้งเดียวfileมันสามารถใช้ไฟล์หลาย ๆ ไฟล์เป็นอาร์กิวเมนต์ได้
phk

@phk เพื่อแสดงความคิดเห็นของคุณ: (1) มันเป็นเรื่องดีที่จะรู้ถึงความช้าที่อาจเกิดขึ้น แต่ฉันไม่เห็นวิธี POSIX ในการป้องกัน; (2) ฉันตั้งสมมติฐานเป็นศูนย์เกี่ยวกับชื่อไฟล์เนื่องจากfindคำสั่งจะนำหน้า./ชื่อไฟล์ใด ๆ ที่ส่งไปยังคำสั่ง shell; (3) การใช้grepเป็นการทดสอบบนfileเอาต์พุตคำสั่งครั้งละหนึ่งเป็นวิธี POSIX เดียวที่ฉันสามารถเห็นเพื่อรับประกันการจัดการชื่อไฟล์ที่ถูกต้องซึ่งอาจมีการขึ้นบรรทัดใหม่
Wildcard

ฉันดูโซลูชัน "POSIX-y" สุดท้ายของคุณและฉันคิดว่ามันฉลาด - แต่คุณคิดว่าfileรองรับการ--mime-encodingตั้งค่าสถานะและ--ตัวคั่นซึ่งPOSIXไม่รับประกัน
Wildcard

2
find . -type f -exec grep -I -q . {} \; -print

นี้จะค้นหาไฟล์ปกติทั้งหมด ( -type f) ในไดเรกทอรีปัจจุบัน (หรือด้านล่าง) ที่grepคิดว่าไม่ว่างเปล่าและไม่ใช่ไบนารี

มันใช้grep -Iเพื่อแยกความแตกต่างระหว่างไฟล์ไบนารีและไฟล์ที่ไม่ใช่ไบนารี -Iธงและจะทำให้grepไปสู่ทางออกด้วยไม่ใช่ศูนย์สถานะออกเมื่อตรวจพบว่าไฟล์ไบนารีคือ ไฟล์ "binary" คือgrepไฟล์ที่มีอักขระอยู่นอกช่วง ASCII ที่พิมพ์ได้

-qตัวเลือกในการgrepจะทำให้มันออกจากที่มีสถานะออกศูนย์ถ้ารูปแบบที่กำหนดจะถูกพบโดยไม่ต้องเปล่งข้อมูลใด ๆ รูปแบบที่เราใช้เป็นจุดเดียวซึ่งจะตรงกับตัวละครใด ๆ

หากพบว่าไฟล์นั้นไม่ใช่แบบไบนารีและหากมีอย่างน้อยหนึ่งตัวอักษรชื่อของไฟล์จะถูกพิมพ์

หากคุณรู้สึกกล้าคุณสามารถเสียบflip -uเข้ากับมันได้เช่นกัน:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;

1

ลองสิ่งนี้:

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

ที่ไหนอาร์กิวเมนต์ของมีgrep '[^ -~]''[^<tab><space>-~]'

หากคุณพิมพ์ในบรรทัดคำสั่งเปลือกพิมพ์Ctrl+ ก่อนV Tabในตัวแก้ไขไม่ควรมีปัญหา

  • '[^<tab><space>-~]'จะจับคู่อักขระใด ๆ ที่ไม่ใช่ข้อความ ASCII (การขึ้นบรรทัดใหม่ถูกละเว้นgrep)
  • -L จะพิมพ์เฉพาะชื่อไฟล์ของไฟล์ที่ไม่ตรงกัน
  • -Zจะชื่อไฟล์ที่ส่งออกคั่นด้วยอักขระ null (สำหรับxargs -0)

เป็นที่น่าสังเกตว่ามี Regex เหมือน Perl grep -P(ถ้ามี) \tให้บริการ หรือใช้การแปลโลแคลหากเชลล์สนับสนุน: $'\t'( bashและzshทำ)
phk

1

ทางเลือกอื่น:

คำสั่ง dos2unix จะแปลงการสิ้นสุดบรรทัดจาก Windows CRLF เป็น Unix LF และข้ามไฟล์ไบนารีโดยอัตโนมัติ ฉันใช้มันซ้ำโดยใช้:

find . -type f -exec dos2unix {} \;

เนื่องจากdos2unixสามารถใช้ชื่อไฟล์หลาย ๆ ตัวเป็นอาร์กิวเมนต์ได้จึงมีประสิทธิภาพมากกว่าที่จะทำfind . -type f -exec dos2unix {} +
Anthon

0

sudo find / (-type f- และ -path '* / git / *' -iname 'README') -exec grep -liI '100644 \ | 100755' {} \; -exec flip -u {} \;

i. (-type f- และ -path '* / git / *' -iname 'README'): ค้นหาไฟล์ภายในพา ธ ที่มีชื่อ git และไฟล์ที่มีชื่อ README หากคุณรู้จักโฟลเดอร์และชื่อไฟล์เฉพาะที่ต้องการค้นหาจะเป็นประโยชน์

คำสั่ง ii. -exec เรียกใช้คำสั่งในชื่อไฟล์ที่สร้างโดย find

สาม.\; บ่งบอกถึงการสิ้นสุดของคำสั่ง

iv. {} คือผลลัพธ์ของไฟล์ / ชื่อโฟลเดอร์ที่พบจากการค้นหาการค้นหาก่อนหน้า

v. คำสั่งหลายรายการสามารถเรียกใช้ในภายหลัง โดยการผนวก -exec "command" \; เช่นกับ -exec flip -u \;

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

คุณสามารถโคลนไดเรกทอรีทดสอบนี้และลองใช้งานได้ที่: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

คำตอบโดยละเอียดเพิ่มเติมได้ที่นี่: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

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