İçeriklerinde% 100 NUL karakterli dosyalar nasıl bulunur?


16

Bu tür dosyaları tanımlayabilen Linux komut satırı komutu nedir?

AFAIK findkomutu (veya grep) yalnızca metin dosyasının içindeki belirli bir dizeyle eşleşebilir . Ama yani ben normal ifade eşleşmesi hangi dosyaların görmek istiyorum, bütün içeriğini eşleştirmek istediğiniz \0+, satır sonu karakteri (ler) yok sayarak . Belki find . cat | grepdeyim işe yarayabilir, ama nasıl grep satırları görmezden (ve ikili dosya olarak muamele) yapmak bilmiyorum.

Arka plan: Her birkaç günde bir, dizüstü bilgisayarım dontuğunda, btrfs bölümüm bilgileri kaybediyor: yazma için açılan dosyalar içeriklerini sıfırlarla değiştiriyor (dosyanın boyutu az ya da çok sağlam kalıyor). Senkronizasyonu kullanıyorum ve bu sahte dosyaların yayılmasını istemiyorum: Onları tanımlamak için bir yola ihtiyacım var, böylece onları yedeklemeden yakalayabilirim.


içinde sayısal sıfır olan dosyalar mı demek istiyorsun?
Rahul Patil

2
Sayısal sıfırlar yerine NULL karakterlerle ilgili olduğunu düşünüyorum.
gertvdijk

10
Buraya bir adım atalım. Birkaç günde bir, dizüstü bilgisayarınız dontuğunda? Neden düzeltmeye çalışmıyoruz o burada, gerçek bir sorun?
Aralık'ta D_Bye

2
@D_Bye bu iyi bir fikir, ancak şu ana kadar çok fazla gelmedi: [ unix.stackexchange.com/questions/57894/…
Adam Ryczkowski

1
-vgrep: 1 ile 255 arasındaki herhangi bir bayt içeren tüm dosyaları filtreleme seçeneğini düşündünüz mü.
ctrl-alt-delor

Yanıtlar:


10

grepPerl normal ifade modunu kullanarak ␀ karakter için yapabilirsiniz :

$ echo -ne "\0\0" > nul.bin
$ echo -ne "\0x\0" > non-nul.bin
$ grep -P "[^\0]" *.bin
Binary file non-nul.bin matches

Yani bunu kullanabilirsiniz:

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done

Kullanarak beklenmedik sonuçlar elde ediyorum GNU grep 2.5.4. Ne olursa olsun ben kullanmak olsun --binary-files=textya --binary-files=binary, bu bir verir truemesela tüm boş olmayan veri değerlerinin, sonucunu. "\0\0", "\0x\0", "abcd"... Ben kullanılan tam koddur: for typ in binary text ;do for dat in '\0\0' '\0x\0' 'abcd' '' ;do printf "$dat" >f; grep --binary-files=$typ -P '[^\0]' f >/dev/null && echo true || echo false; done; done
Peter.O

1
Şimdi daha da denedim GNU grep) 2.10. Bu sonraki sürüm beklenen sonuçları veriyor ... bu nedenle, gecikmiş +1
Peter.O

1
Önemli olan printf '\0\n\0\0\n\n' > fileveya printf '\n' > fileiçin oluşturulmuş bir dosyada başarısız olur .
Stéphane Chazelas

2
@ StéphaneChazelas OP "satır sonu karakterlerini yoksaymak" dedi. Bu nedenle, yalnızca \0ve \nkarakterlerden (her ikisinin de sıfırı) oluşan herhangi bir dosya eşleşir.
l0b0

6

Sorunun kökenini bulmak için D_Bye'nin söylediklerine katılıyorum.

Her neyse, bir dosyanın yalnızca içerip içermediğini \0ve / veya \nkullanabileceğinizi kontrol etmek için tr:

<file tr -d '\0\n' | wc -c

Boş / yeni satır ve boş dosyalar için 0 döndürür.


2
tr -d '\0\n'yeni satır sorununu çözer, bu da yalnızca çıktıda listelenen boş dosyaların sorununu (?) bırakır ... Her dosyanın her baytını (bir sorun olabilir veya olmayabilir)
işler

@ Peter.O: Newline şartını kaçırdım, teşekkürler. Bu çözüm çok optimize edilmemiştir ve çok fazla veri üzerinde çalışacaksa, eşleşmeyen baytları bulmaya devam eden bir çözümle daha iyi olur.
Thor

Çok iyi çalışıyor. Benim durumum sadece sıfır uzunluktaki dosyaları hariç tutmak için emin olmak zorunda kaldı. Teşekkür ederim.
Adam Ryczkowski

1
Ancak bu aynı zamanda yeni satırları olan dosyaları "boş" olarak sayar.
Chris Down

1
@ChrisDown: Cevap metnini ne yaptığına dair netleştirdim. OP'nin yalnızca satırsonu dosyalar ile ne yapmak istediği net değil.
Thor

5

Bu dosyaların seyrek olduğundan şüpheleniyorum, yani kendilerine ayrılmış disk alanı yok, sadece bir dosya boyutu belirtiyorlar (onlar duiçin 0 rapor edecekler).

Bu durumda, GNU find ile şunları yapabilirsiniz (hiçbir dosya yolunun yeni satır karakteri içermediği varsayılarak):

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-

İyi bir nokta. Bunun hakkında hiç düşünmedim. Deneyeceğim. Kullanmak du, dosya sistemindeki her bir dosyanın içeriğini çizmemeyi engelleyecektir, bu nedenle tüm prosedürün tamamlanması 30 dakikadan fazla sürmez.
Adam Ryczkowski

(ve printf %byukarısı neyin durapor edeceğini rapor eder)
Stéphane Chazelas

Ben değiştirecek -size +0kadar -size +1sıfır uzunluk dosyaları sonuçlarının dışında tutulur böylece. Ayrıca \nyollarında bulunan dosyalar bu komut için sorunlara neden olur.
Tyson

@Tyson -size +0, kesinlikle 0'dan büyük boyutlar içindir. 512'den kesinlikle büyük boyutlar için -size +1olacaktır. Yeni satır sınırlamasından daha önce bahsedilmişti.
Stéphane Chazelas

@ StéphaneChazelas Beni bilgilendirdiğin için teşekkürler, haklısın -size +1. Ben çözdüm benim cevap. :-)
Tyson

4

İşte yapabileceğiniz küçük bir python programı:

import sys

def only_contains_nulls(fobj, chunk_size=1024):
    first = True
    while True:
        data = fobj.read(chunk_size)
        if not data:
            if first:
                return 1  # No data
            else:
                return 0
        if data.strip("\0"):
            return 1
        first = False

if __name__ == '__main__':
    with open(sys.argv[1]) as f:
        sys.exit(only_contains_nulls(f))

Ve eylemde:

$ printf '\0\0\0' > file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Only nulls
$ printf a >> file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Non-null characters

Sen bulmak en kullanarak birden fazla dosya kontrol edebilirsiniz -exec, xargs, GNU parallelve benzeri programlar. Alternatif olarak, ele alınması gereken dosya adları yazdırılır:

files=( file1 file2 )
for file in "${files[@]}"; do
    ./onlynulls "$file" || printf '%s\n' "$file"
done

Bunun çıktısını başka bir programa aktaracaksanız, dosya adlarının yeni satırlar içerebileceğini aklınızda bulundurun, bu yüzden farklı şekilde (uygun şekilde, ile \0) sınırlandırmanız gerekir .

Çok fazla dosyanız varsa, aynı anda yalnızca bir dosya okuduğu için paralel işleme seçeneği kullanmak daha iyi olacaktır.


2
(: Örneğin sıfır boyutlu dosyalar dikkat /etc/nologin, ~/.hushlogin, .nomedia, ...) Bu cevap tarafından yanlış edilir.
Tyson

@Tyson Bunu işaret ettiğiniz için teşekkürler! Sadece düzelttim.
Chris Down

3

Yalnızca null karakterleri '\ 0' ve yeni satır karakterleri '\ n' içeren dosyaları bulun. İçinde sed nedenleri her dosya bir çizgide herhangi boş olmayan karakteri bulma hemen sonra çıkmak için arayın.
q

find -type f -name 'file-*' |
  while IFS= read -r file ;do 
      out=$(sed -n '1=; /^\x00\+$/d; i non-null
                      ; q' "$file")
      [[ $out == "1" ]] &&  echo "$file"
  done

Test dosyaları yapın

> file-empty
printf '%s\n' 'line1' 'line2' 'line3'      > file-with-text           
printf '%4s\n' '' '' xx | sed 's/ /\x00/g' > file-with-text-and-nulls
printf '%4s\n' '' '' '' | sed 's/ /\x00/g' > file-with-nulls-and-newlines
printf '%4s'   '' '' '' | sed 's/ /\x00/g' > file-with-nulls-only

çıktı

./file-with-nulls-and-newlines
./file-with-nulls-only

Ya -print0argüman eksik gibi görünüyor findya da IFS=parça berbat. Amaçlanan sınırlayıcı neydi?
Tyson

3

Bu tek satırlık GNU kullanarak% 100 nul dosyaları bulmak için en etkili yoludur find, xargsve grep(PCRE desteği ile inşa edilmiştir ikincisi varsayarak):

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00]" --

Bu yöntemin sağlanan diğer cevaplara göre avantajları şunlardır:

  • seyrek olmayan dosyalar aramaya dahil edilir.
  • okunamayan dosyalar grep'e aktarılmaz, böylece Permission denieduyarılar önlenir.
  • grepboş olmayan bir bayt bulduktan sonra dosyalardan veri okumayı durduracaktır ( LC_ALL=Cher baytın bir karakter olarak yorumlandığından emin olmak için kullanılır ).
  • boş dosyalar (sıfır bayt) sonuçlara dahil edilmez.
  • daha az grepişlem birden fazla dosyayı verimli bir şekilde kontrol eder.
  • yeni satır içeren ya da ile başlayan yollar -doğru şekilde işlenir.
  • Python / Perl içermeyen çoğu gömülü sistemde çalışır.

Geçme -Zseçeneği grepkullanılarak xargs -r0 ...daha ileri eylemler% 100 nul dosyalar üzerinde gerçekleştirilmesine olanak tanır (örneğin: temizleme):

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00]" -- |
  xargs -r0 rm --

Ayrıca , simgeleri takip etmekten kaçınmak ve dosya sistemlerinden geçmekten kaçınmak için findseçenekleri kullanmanızı öneririm (örneğin: uzak bağlar, aygıt ağaçları, bağlama bağları, vb.).-P-xdev

İçin hat sonu karakter (ler) göz ardı (Bunun hiç de iyi bir fikir olduğunu sanmıyorum gerçi), aşağıdaki varyant çalışması gerekir:

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00\r\n]" --

Yedeklenmelerini önlemek için istenmeyen dosyaları (% 100 boş / yeni satır karakterleri) kaldırmak da dahil olmak üzere hepsini bir araya getirmek:

find -P . -xdev -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00\r\n]" -- |
  xargs -0 rm --

Boş dosyalar (sıfır bayt) eklemenizi önermiyorum, genellikle çok özel amaçlar için var olurlar .


Pek çok alternatifin en hızlısı olmak cesur bir iddia. Bir kıyaslama eklerseniz cevabınızı kabul edilmiş olarak işaretleyeceğim :-)
Adam Ryczkowski

Böyle bir kıyaslama, çeşitli disk alt sistemlerinin performansı da dahil olmak üzere birçok faktöre bağlı olacaktır.
Tyson

Tabii ki, ama her şey hiç yoktan iyidir. Çeşitli yaklaşımlar CPU kullanımını farklı şekilde optimize eder, bu nedenle SSD'de veya hatta önbellek dosyalarında karşılaştırmak mantıklıdır. Şu anda üzerinde çalıştığınız makineyi alın, ne olduğunu bir cümle yazın (CPU tipi, çekirdek sayısı, RAM, sabit sürücü tipi), dosya setini açıklayın (örn. Çekirdek kaynak klonu + \0içinde 900MB delik ile dolu 1GB dosya ) ve sonuçların zamanlamasını sunar. Kıyaslama sizin için ikna edici bir şekilde yaparsanız, büyük olasılıkla hepimiz için mahkum olacaktır
Adam Ryczkowski

"çoğu gömülü sistem" in GNU yardımcı programları yoktur. Muhtemelen meşgul kutusu olanlar.
Stéphane Chazelas

-P, içindeki varsayılan değerdir find. Symlinks'i takip etmek istiyorsanız, -L/ -follow. POSIX'in bu seçeneği belirtmediğini göreceksiniz find(POSIX, birkaç komut için -P / -H / -L'yi kullanan kişi olsa bile).
Stéphane Chazelas

0

GNU sed kullanmak için -z, bir satırı sıfır sonlu dizeler olarak tanımlayan ve aşağıdaki gibi boş satırlarla eşleşen ve silen seçeneği kullanabilirsiniz :

if [ "$( sed -z '/^$/d' "$file" | head -c 1 | wc -c )" -eq 0 ]; then
    echo "$file contains only NULL!"
fi

Aradaki head komutu sadece bir optimizasyon.


-1

piton

Tek dosya

Takma adı tanımlayın:

alias is_binary="python -c 'import sys; sys.exit(not b\"\x00\" in open(sys.argv[1], \"rb\").read())'"

Dene:

$ is_binary /etc/hosts; echo $?
1
$ is_binary `which which`; echo $?
0

Birden çok dosya

Tüm ikili dosyaları özyinelemeli olarak bulun:

IS_BINARY='import sys; sys.exit(not b"\x00" in open(sys.argv[1], "rb").read())'
find . -type f -exec bash -c "python -c '$IS_BINARY' {} && echo {}" \;

İkili olmayan tüm dosyaları bulmak için &&ile değiştirin ||.


1
Soru içeren dosyaları tanımlamak istedi ancak nul karakterleri (gözardı yeni satır) içeren tanımlar dosyaları burada verilen Python kodunu herhangi nul karakterleri.
Tyson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.