Znajdowanie wszystkich plików „niebinarnych”

43

Czy można użyć findpolecenia, aby znaleźć wszystkie pliki „niebinarne” w katalogu? Oto problem, który próbuję rozwiązać.

Otrzymałem archiwum plików od użytkownika systemu Windows. To archiwum zawiera kod źródłowy i pliki obrazów. Nasz system kompilacji nie działa dobrze z plikami z zakończeniami linii systemu Windows. Mam program wiersza polecenia ( flip -u), który przerzuca zakończenia linii między * nix a windows. Więc chciałbym zrobić coś takiego

find . -type f | xargs flip -u

Jeśli jednak polecenie to zostanie uruchomione dla pliku obrazu lub innego binarnego pliku multimedialnego, plik zostanie uszkodzony. Zdaję sobie sprawę, że mogę zbudować listę rozszerzeń plików i filtrować z tym, ale wolę mieć coś, co nie zależy ode mnie, aby aktualizować tę listę.

Czy istnieje sposób na znalezienie wszystkich plików niebinarnych w drzewie katalogów? A może istnieje alternatywne rozwiązanie, które powinienem rozważyć?

Alan Storm
źródło
1
Możesz użyć tego filenarzędzia gdzieś w skrypcie / potoku, aby ustalić, czy plik to dane, czy tekst
lk-
1
Co rozumiesz przez binarny (wszystko na nowoczesnym komputerze jest binarne). Zgaduję, że używasz odróżnienia od starego systemu operacyjnego C / PM, który miał pliki tekstowe i binarne. Pliki tekstowe mogą mieć dowolną długość, ale muszą kończyć się ctrl-z, a pliki binarne muszą być wielokrotnością bloku 512 bajtów. Jeśli tak, masz na myśli plik tekstowy. (Zauważam również, że piszesz o zakończeniu linii w plikach niebinarnych, to również sugeruje, że są to pliki tekstowe). Czy to prawda?
ctrl-alt-delor
Wszystkie pliki są binarne, to tylko kwestia interpretacji. Czy pytasz, jak znaleźć pliki tekstowe?
ctrl-alt-delor
@richard Pochodzę z epoki, w której nazywaliśmy pliki przeznaczone do interpretacji jako zwykły tekst , a wszystkie inne pliki (obrazy, dokumenty do edycji tekstu itp.) są binarne. Wiem, że to wszystko jest tylko jedynkami i zerami pod maską :)
Alan Storm
1
Ach, rozumiem, co masz na myśli przez moje warunki - użyję binarnego / tekstowego w przyszłości, aby uniknąć nieporozumień. Re: rzecz \ r \ n - rozumiem, że to znaki ASCII dla powrotu karetki maszyny do pisania (przejście na początek linii) i przesunięcia linii (przejście w dół o jedną linię). Tak więc \ r \ n jest „bardziej dokładnym” modelem rzeczy fizycznej w świecie rzeczywistym, dla której była postać końca linii. Przed systemem OS X komputery Mac używały tylko do tego celu. Zazwyczaj odpisuję to jako „arbitralne wybory dokonane w pośpiechu, z którymi wciąż mamy do czynienia”
Alan Storm

Odpowiedzi:

20

Użyłem filei potokowałem dane wyjściowe do grep lub awk, aby znaleźć pliki tekstowe, a następnie wyodrębniłem tylko część pliku filewyjściowego i potokowałem to do xargs.

coś jak:

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

Zauważ, że grep wyszukuje „tekst ASCII” zamiast zwykłego „tekstu” - prawdopodobnie nie chcesz zadzierać z dokumentami Rich Text lub plikami tekstowymi Unicode itp.

Możesz także użyć find(lub cokolwiek innego) do wygenerowania listy plików do sprawdzenia za pomocą file:

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

-d'\n'Argument xargs sprawia xargs traktować każdą linię wejściowego jako osobny argument zatem catering dla nazwy plików ze spacjami i innymi znakami problematyczne. znaczy to alternatywą xargs -0gdy źródłem nie, czy też nie może wytwarzać moc NULL rozdzielone (na przykład find„s -print0opcja). Zgodnie z dziennikiem zmian xargs otrzymał opcję -d/ --delimiterwe wrześniu 2005 roku, więc powinien znajdować się w każdej nie starożytnej dystrybucji Linuksa (nie byłem pewien, dlatego sprawdziłem - po prostu niejasno pamiętałem, że był to „ostatni” dodatek).

Zauważ, że wysuw wiersza jest prawidłowym znakiem w nazwach plików, więc to się zepsuje, jeśli w nazwach plików znajdują się źródła. Dla typowych użytkowników Uniksa jest to patologicznie szalone, ale nie jest niespotykane, jeśli pliki pochodzą z komputerów Mac lub Windows.

Pamiętaj też, że filenie jest to idealne. Jest bardzo dobry w wykrywaniu rodzaju danych w pliku, ale czasami może się zdezorientować.

W przeszłości z powodzeniem korzystałem z wielu odmian tej metody.

cas
źródło
1
Dzięki za to rozwiązanie! Z jakiegoś powodu filewyświetla się English textraczej niż ASCII textw moim systemie Solaris, więc odpowiednio zmodyfikowałem tę część. Ponadto zastąpiłem awk -F: '{print $1}'odpowiednikiem cut -f1 -d:.
Andrew Cheong,
3
warto powiedzieć grep -Ifiltry binarne
xenoterracide
Szukanie słowa textpowinno wystarczyć. Spowoduje to również pobranie fileopisów takich jak ASCII Java program textlub HTML document textlub troff or preprocessor input text.
user1024,
Moja odpowiedź jest częściowo odpowiedzią / ulepszeniem tej odpowiedzi. Bardzo dobra uwaga na temat grepowania, ASCII textaby uniknąć pomieszania RTF.
Wildcard,
1
xenoterracide: Uratowałeś mi życie człowieku! Tylko flaga - I i BINGO
Sergio Abreu
9

Nie. Nie ma nic specjalnego w pliku binarnym lub niebinarnym. Możesz użyć heurystyki, np. „Zawiera tylko znaki w 0x01–0x7F”, ale to wywoła pliki tekstowe z plikami binarnymi bez znaków ASCII i pechowymi plikami tekstowymi.

Teraz, gdy zignorujesz to ...

pliki zip

Jeśli pochodzi od użytkownika systemu Windows jako plik zip, format zip obsługuje oznaczanie plików jako plików binarnych lub tekstowych w samym archiwum. Możesz użyć -aopcji rozpakowania, aby zwrócić na to uwagę i przekonwertować. Oczywiście zapoznaj się z pierwszym akapitem, aby dowiedzieć się, dlaczego nie jest to dobry pomysł (program zip mógł pomylić się podczas tworzenia archiwum).

zipinfo powie ci, które pliki są binarne (b) lub tekstowe (t) na liście zipfile.

inne pliki

Polecenie file sprawdzi plik i spróbuje go zidentyfikować. W szczególności prawdopodobnie -iprzydatna będzie opcja (wyjściowy typ MIME); konwertuj tylko pliki typu text / *

derobert
źródło
6

Ogólne rozwiązanie do przetwarzania plików innych niż binarne bashprzy użyciu 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)

Skontaktowałem się z autorem narzędzia do plików i dodał on sprytny -00parametr w wersji 5.26 (wydany 2016-04-16, jest np. W obecnym Archu i Ubuntu 16.10), który drukuje file\0result\0dla wielu dostarczonych do niego plików jednocześnie, w ten sposób możesz to zrobić na przykład:

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

( awkCzęścią jest odfiltrowanie każdego pliku, który nie jest binarny. ORSJest separatorem wyjściowym.)

Oczywiście można go również używać w pętli:

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}')

Na podstawie tego i poprzedniego stworzyłem mały bashskrypt do odfiltrowywania plików binarnych, który wykorzystuje nową metodę przy użyciu -00parametru filew nowszych wersjach i wraca do poprzedniej metody w starszych wersjach:

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

Lub tutaj bardziej POSIX-y, ale wymaga obsługi 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
phk
źródło
6

Przyjęta odpowiedź nie znalazła dla mnie wszystkich. Oto przykład użycia grep -Ido ignorowania plików binarnych i ignorowania wszystkich ukrytych plików ...

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

Tutaj jest używany w praktycznej aplikacji: dos2unix

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

phyatt
źródło
4

Odpowiedź Cas jest dobra, ale zakłada rozsądne nazwy plików; w szczególności zakłada się, że nazwy plików nie będą zawierać nowych linii.

Nie ma powodu, aby przyjmować to założenie, ponieważ jest to całkiem proste (i moim zdaniem czystsze), aby poprawnie obsługiwać tę sprawę:

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

findKomenda czyni korzystanie jedynie z POSIX określonych cech . Używanie -execdo uruchamiania dowolnych poleceń jako testów logicznych jest proste, niezawodne (poprawnie obsługuje nieparzyste nazwy plików) i bardziej przenośne niż -print0.

W rzeczywistości wszystkie części polecenia są określone przez POSIX, z wyjątkiem flip.

Pamiętaj, że filenie gwarantuje to dokładności wyników, które zwraca. Jednak w praktyce grepowanie dla „tekstu ASCII” na wyjściu jest dość niezawodne.

(Być może brakuje niektórych plików tekstowych, ale jest bardzo mało prawdopodobne, aby nieprawidłowo zidentyfikować plik binarny jako „tekst ASCII” i zmienić go w błąd - dlatego popełniamy błąd z ostrożnością.)

Dzika karta
źródło
Plik bez argumentów callsmoże być dość powolny, np. W przypadku filmów wideo opowie ci wszystko o kodowaniu.
phk
Zakładasz również, że żaden plik nie zaczyna się od -.
phk
I nie widzę powodu, dla którego po prostu nie wykonalibyście pojedynczego wywołania file, może ono przyjmować wiele plików jako argumenty.
phk
@phk, aby odpowiedzieć na twoje komentarze: (1) dobrze jest znać potencjalną powolność, ale nie widzę żadnego sposobu POSIX, aby temu zapobiec; (2) Przyjmuję zerowe założenia dotyczące nazw plików, ponieważ findpolecenie będzie poprzedzać ./dowolną nazwę pliku przekazywaną do polecenia powłoki; (3) Używanie grepjako testu pojedynczego filewyjścia polecenia jest jedynym sposobem POSIX, jaki widzę, aby zagwarantować poprawną obsługę nazw plików, które mogą zawierać znaki nowej linii.
Wildcard,
Przejrzałem twoje ostatnie rozwiązanie „POSIX-y” i myślę, że jest sprytne - ale zakładasz, że fileobsługuje --mime-encodingflagę i --separator, z których żaden nie jest gwarantowany przez POSIX .
Wildcard
2
find . -type f -exec grep -I -q . {} \; -print

Spowoduje to znalezienie wszystkich zwykłych plików ( -type f) w bieżącym katalogu (lub poniżej), które grepmogą być niepuste i binarne.

Służy grep -Ido rozróżniania plików binarnych i niebinarnych. -IFlag i spowoduje, że grepdo wyjścia ze stanem wyjściowym niezerową, gdy wykryje, że plik jest binarny. Plik „binarny” jest, zgodnie z tym grep, plikiem zawierającym znak spoza zakresu drukowalnego ASCII.

-qOpcja grepspowoduje to, aby wyjść z kodem zerowym, jeśli dany wzorzec zostanie znaleziony, bez emitowania żadnych danych. Wzór, którego używamy, to pojedyncza kropka, która będzie pasować do dowolnego znaku.

Jeśli okaże się, że plik nie ma charakteru binarnego i jeśli zawiera co najmniej jeden znak, drukowana jest nazwa pliku.

Jeśli czujesz się odważny, możesz również podłączyć flip -udo niego:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
Kusalananda
źródło
1

Spróbuj tego :

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

Gdzie argument grep '[^ -~]'jest '[^<tab><space>-~]'.

Jeśli wpiszesz go w linii poleceń powłoki, wpisz Ctrl+ Vprzed Tab. W edytorze nie powinno być problemu.

  • '[^<tab><space>-~]'dopasuje dowolny znak, który nie jest tekstem ASCII (znaki powrotu karetki są ignorowane przez grep).
  • -L wypisze tylko nazwę pliku, który nie pasuje
  • -Zwyświetli nazwy plików oddzielone znakiem null (dla xargs -0)
Vouze
źródło
Warto zauważyć, że z Regex-podobnym do Perla grep -P(jeśli jest dostępny) \tjest dostępny. Alternatywnie, stosując tłumaczenie lokalizacji jeśli podpory powłoki w jakiej: $'\t'( bashi zshrobią).
phk
1

Alternatywne rozwiązanie:

Polecenie dos2unix konwertuje zakończenia linii z Windows CRLF na Unix LF i automatycznie pomija pliki binarne. Stosuję go rekurencyjnie, używając:

find . -type f -exec dos2unix {} \;
Iskra
źródło
Ponieważ dos2unixjako argument można przyjąć wiele nazw plików, jest to o wiele bardziej wydajnefind . -type f -exec dos2unix {} +
Anthon
0

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

i. (-type f -and -path '* / git / *' -iname 'README'): wyszukuje pliki w ścieżce zawierającej nazwę git i plik o nazwie README. Jeśli znasz jakiś konkretny folder i nazwę pliku do wyszukania, przyda się.

ii.-exec uruchamia polecenie na nazwie pliku wygenerowanej przez find

iii. \; wskazuje koniec polecenia

iv. {} jest wyjściem pliku / nazwy folderu znalezionego podczas poprzedniego wyszukiwania wyszukiwania

v.Następnie można uruchamiać wiele poleceń. Dołączając polecenie -exec „command” \; na przykład z -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. 

możesz sklonować ten katalog testowy i wypróbować go: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

bardziej szczegółowa odpowiedź tutaj: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

alpha_989
źródło