Glob'u "bul" a dönüştür


11

Ben tekrar tekrar bu sorun vardı: Tam olarak doğru dosyaları eşleşen, ancak neden bir glob var Command line too long. Her seferinde bir kombinasyona dönüştürdüğümde findve grepbu durum için işe yarıyor, ancak% 100 eşdeğeri değil.

Örneğin:

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

Globları findfarkında olmadığım ifadelere dönüştürmek için bir araç var mı ? Veya findbir alt dizede aynı bir küreyi eşleştirmeden küreyi eşleştirme seçeneği var mı (örneğin foo/*.jpgeşleşmesine izin verilmiyor bar/foo/*.jpg)?


Köşeli ayracı genişletin ve sonuçtaki ifadeleri -pathveya ile kullanabilirsiniz -ipath. find . -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg'çalışması gerekir - bununla eşleşmesi dışında /fooz/blah/bar/quuxA/pic1234d.jpg. Bu sorun olacak mı?
muru

Evet, bu bir sorun olacak. % 100 eşdeğeri olmalıdır.
Ole Tange

Sorun şu ki hiçbir fikrimiz yok, tam olarak fark nedir. Deseniniz oldukça iyi.
peterh - Monica

Uzatma postanızı soruya cevap olarak ekledim. Umarım o kadar da kötü değildir.
peterh - Monica'yı geri yükle

Yapamam echo <glob> | cat, bash bilgimi varsayarsak, yankı yerleşiktir ve bu nedenle maksimum komut sınırına sahip değildir
Ferrybig

Yanıtlar:


15

Sorun, argüman-listesi-çok-uzun bir hata alıyorsanız, bir döngü veya yerleşik bir kabuk kullanın. İken command glob-that-matches-too-muchdışarı hata olabilir, for f in glob-that-matches-too-muchsadece bunu yapabilirsiniz, değil:

for f in foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
do
    something "$f"
done

Döngü dayanılmaz derecede yavaş olabilir, ancak çalışması gerekir.

Veya:

printf "%s\0" foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg |
  xargs -r0 something

( printfçoğu kabukta yerleşik olarak, yukarıdaki execve()sistem çağrısının sınırlandırılması etrafında çalışır )

$ cat /usr/share/**/* > /dev/null
zsh: argument list too long: cat
$ printf "%s\n" /usr/share/**/* | wc -l
165606

Ayrıca bash ile çalışır. Bunun tam olarak nerede belgelendiğinden emin değilim.


Hem Vim en glob2regpat()ve Python en fnmatch.translate()regexes globs dönüştürebilir, ancak her iki de kullanmak .*için *karşıdan karşıya eşleşen /.


Eğer bu doğruysa, o zaman değiştirilmesi somethingile echobunu yapmak gerektiğini.
Ole Tange

1
@OleTange Bu yüzden önerdim printf- echobinlerce kez aramaktan daha hızlı olacak ve daha fazla esneklik sunacak .
muru

4
execDış komutlar için geçerli olan, iletilebilen argümanlar üzerinde bir sınır vardır cat; ancak bu sınır gibi kabuk yerleşik komutları için geçerli değildir printf.
Stephen Kitt

1
@OleTange printfBir yerleşik olduğu için çizgi çok uzun değil ve kabuklar, argümanları numaralandırmak için kullandıkları argümanları sağlamak için muhtemelen aynı yöntemi kullanıyorlar for. catbir yerleşik değildir.
muru

1
Teknik olarak, mkshnerede printfyerleşik olmadığı gibi kabuklar ve ksh93nerede yerleşik cat(veya olabilir) gibi kabuklar vardır . Ayrıca bakınız zargsde zshbaşvurmak zorunda kalmadan etrafında işe xargs.
Stéphane Chazelas

9

find( -name/ -pathstandart tahminler için) tıpkı globlar gibi joker karakterler kullanır ( {a,b}glob operatörü değildir; genişlemeden sonra iki glob alırsınız). Temel fark, eğik çizgilerin (ve özellikle işlenmeyen nokta dosyalarının ve dizinlerin find) işlenmesidir . *globs içinde birkaç dizine yayılmaz. */*/*2 seviyeye kadar dizin listelenmesine neden olur. Bir ekleme -path './*/*/*', en az 3 düzey derinlikte olan ve findherhangi bir dizinin içeriğini herhangi bir derinlikte listelemeyi durduramayacak olan dosyalarla eşleşir .

Bu ayrıntı için

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

birkaç glob, çevirmek kolaydır, derinlik 3'te dizinler istersiniz, böylece kullanabilirsiniz:

find . -mindepth 3 -maxdepth 3 \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

(veya -depth 3bazı finduygulamalarla). Veya POSIXly:

find . -path './*/*/*' -prune \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

Hangi olanlar güvence altına alacak *ve ?ulaşamasa /karakterleri.

( findglob'ların aksine, dizinlerin dışındaki dizinlerin içeriğinifoo*bar , geçerli dizinde bulunanlar dosyaların listesini sıralamaz. Ancak, eşleştirilen sorunun [A-Z]veya geçersiz karakterlerle ilgili */ ?belirtilmemişse, aynı dosya listesini alırsınız).

Ancak her durumda, muru'nun gösterdiği gibi , findsadece sistem listesini sınırlamak için birkaç dosyaya bölmek için başvurmaya gerek yoktur execve(). Bunun gibi zsh(ile zargs) veya ksh93(ile command -x) bazı mermilerin yerleşik desteği vardır.

İle zsh (globları aynı zamanda -type fdiğer tahminlere de eşdeğerdir find), örneğin:

autoload zargs # if not already in ~/.zshrc
zargs ./foo*bar/quux[A-Z](|.bak)/pic[0-9][0-9][0-9][0-9]?.jpg(.) -- cmd

( (|.bak)aksine bir glob operatörüdür {,.bak}, (.)glob niteleyicisi find's -type f,oN olduğu gibi sıralama atlamak için orada find, Ddosyaları nokta (bu glob için geçerli değildir) içerecek şekilde)


¹ İçin find Dizin ağacını globların yaptığı gibi taramak için şöyle bir şeye ihtiyacınız var:

find . ! -name . \( \
  \( -path './*/*' -o -name 'foo*bar' -o -prune \) \
  -path './*/*/*' -prune -name 'pic[0-9][0-9][0-9][0-9]?.jpg' -exec cmd {} + -o \
  \( ! -path './*/*' -o -name 'quux[A-Z]' -o -name 'quux[A-Z].bak' -o -prune \) \)

Yani budamak haricinde 1. düzeyde tüm dizinleri foo*barolanlar ve tüm hariç 2 düzeyinde quux[A-Z]veya quux[A-Z].bakdaha sonra olanlar ve seçmek pic...3. seviyede olanları (ve bu düzeydeki tüm dizinleri erik).


3

Gereksinimlerinize uygun bulmak için bir normal ifade yazabilirsiniz:

find . -regextype egrep -regex './foo[^/]*bar/quux[A-Z](\.bak)?/pic[0-9][0-9][0-9][0-9][^/]?\.jpg'

İnsan hatalarını önlemek için bu dönüşümü yapan bir araç var mı?
Ole Tange

Hayır, ama sadece kaçarlarsa yapılan değişiklikleri ., isteğe bağlı maçı eklemek .bakve değişim *için [^/]*yolları eşleşmemelerine gibi / foo / foo / bar vb
sebasth

Ancak dönüşümünüz bile yanlış. ? , [^ /] olarak değiştirilmedi. Bu tam olarak kaçınmak istediğim insan hatasıdır.
Ole Tange

1
Ben egrep ile düşünüyorsun, kısaltabilirsiniz [0-9][0-9][0-9][0-9]?için[0-9]{3,4}
wjandrea


0

Diğer cevabımdaki nota genelleme yaparak, sorunuza daha doğrudan bir cevap olarak sh, glob'u bir findifadeye dönüştürmek için bu POSIX komut dosyasını kullanabilirsiniz :

#! /bin/sh -
glob=${1#./}
shift
n=$#
p='./*'

while true; do
  case $glob in
    (*/*)
      set -- "$@" \( ! -path "$p" -o -path "$p/*" -o -name "${glob%%/*}" -o -prune \)
      glob=${glob#*/} p=$p/*;;
    (*)
      set -- "$@" -path "$p" -prune -name "$glob"
      while [ "$n" -gt 0 ]; do
        set -- "$@" "$1"
        shift
        n=$((n - 1))
      done
      break;;
  esac
done
find . "$@"

Tek bir standart shglob ile kullanılmak üzere (örneğin, küme ayracı genişlemesi kullanan örneğin iki glob değil ):

glob2find './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' \
  -type f -exec cmd {} +

(nokta-dosyaları görmezden veya dot-dizinleri hariç etmediğini .ve ..ve dosyaları does not sıralama listesi).

Bu yalnızca geçerli dizine göre glob'larla çalışır, no .veya ..bileşenleri yoktur . Biraz çaba ile, herhangi bir glob, bir glob daha fazla genişletebilirsiniz ... Bu da optimize edilebilir, böylece bir desen için olduğu gibi aynı glob2find 'dir/*'görünmüyor dir.

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.