Bir unix / linux kabuğunda desen eşleşmesi yaparken ters veya negatif joker karakterleri nasıl kullanabilirim?


325

Diyelim ki adları 'Müzik' kelimesini içeren dosyalar ve klasörler hariç bir dizinin içeriğini kopyalamak istiyorum.

cp [exclude-matches] *Music* /target_directory

Bunu başarmak için [hariç tutmalar] yerine ne gitmeli?

Yanıtlar:


375

Bash'de extglob, bu seçeneği etkinleştirerek yapabilirsiniz (elbette hedef dizini lsile değiştirin cpve ekleyin)

~/foobar> shopt extglob
extglob        off
~/foobar> ls
abar  afoo  bbar  bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob  # Enables extglob
~/foobar> ls !(b*)
abar  afoo
~/foobar> ls !(a*)
bbar  bfoo
~/foobar> ls !(*foo)
abar  bbar

Daha sonra extglob'u şununla devre dışı bırakabilirsiniz:

shopt -u extglob

14
Bu özelliği beğendim:ls /dir/*/!(base*)
Erick Robertson

6
Her şeyi ( ) nasıl dahil edersiniz ve! (B ) 'yi nasıl hariç tutarsınız ?
Elijah Lynn

4
Diyelim ki f, dışındaki her şeyle nasıl eşleşirsiniz foo?
Noldorin

8
Bu neden varsayılan olarak devre dışıdır?
weberc2

3
içinde ünlem işareti olan dosyaları aramanız gerekirse shopt -o -u histexpand - varsayılan olarak, extglob varsayılan olarak kapalıdır, böylece histexpand ile etkileşime girmez, dokümanlarda bunun neden böyle olduğunu açıklar. foo ile başlayan her şeyi eşleştirin: f! (oo), tabii ki 'food' eşleşir ('foo' ile başlayan şeyleri durdurmak için f! (oo *) gerekir ya da kurtulmak istiyorsanız '.foo' ile biten veya öneklenen bazı şeyler : myprefix! ( .foo) (myprefixBLAH ile eşleşir ancak myprefixBLAH.foo ile eşleşmez)
osirisgothra

227

extglobKabuk seçeneği size komut satırında daha güçlü desen eşleştirme verir.

İle shopt -s extglobaçarsınız ve ile kapatırsınız shopt -u extglob.

Örneğinizde, başlangıçta şunları yaparsınız:

$ shopt -s extglob
$ cp !(*Music*) /target_directory

Mevcut tüm dahili uçlu glob bing operatörleri (alıntı man bash):

Extglob kabuk seçeneği shopt yerleşik kullanılarak etkinleştirilirse, birkaç genişletilmiş desen eşleştirme operatörü tanınır. Bir desen listesi, | ile ayrılmış bir veya daha fazla desenin listesidir. Kompozit desenler, aşağıdaki alt şablonlardan biri veya daha fazlası kullanılarak oluşturulabilir:

  • ? (kalıp listesi)
    Verilen kalıpların sıfır veya bir tekrarıyla eşleşir
  • * (desen listesi)
    Verilen desenlerin sıfır veya daha fazla örneğiyle eşleşir
  • + (desen listesi)
    Verilen desenlerin bir veya daha fazla örneğiyle eşleşir
  • @ (desen listesi)
    Belirtilen modellerden biriyle eşleşir
  • ! (kalıp listesi)
    Verilen kalıplardan biri dışındaki herhangi bir şeyle eşleşir

Örneğin, geçerli dizindeki olmayan .cveya .hdosyalardaki tüm dosyaları listelemek isterseniz, şunları yaparsınız:

$ ls -d !(*@(.c|.h))

Tabii ki, normal kabuk globing işe yarıyor, bu nedenle son örnek de şöyle yazılabilir:

$ ls -d !(*.[ch])

1
-D'nin nedeni nedir?
Büyük McLargeHuge

2
@Koveras .cveya .hdosyalardan birinin bir dizin olduğu durumda.
tzot

@DaveKennedy Geçerli dizindeki her şeyi listeler D, ancak dizinde bulunabilecek alt dizinlerin içeriğini listelemez D.
spurra

23

Bash değil (bildiğim), ama:

cp `ls | grep -v Music` /target_directory

Bunun tam olarak aradığınız şey olmadığını biliyorum, ancak örneğinizi çözecek.


Varsayılan ls her satır için birden fazla dosya koyacaktır, bu da muhtemelen doğru sonuçları vermeyecektir.
Daniel Bungert

10
Sadece stdout bir terminal olduğunda. Bir boru hattında kullanıldığında, ls her satıra bir dosya adı yazdırır.
Adam Rosenfield

ls bir uçbirime çıktı verirse her satıra yalnızca birden fazla dosya koyar. Kendiniz deneyin - "ls | less" satır başına asla birden fazla dosya olmaz.
SpoonMeiser

3
Boşluk içeren dosya adları (veya diğer beyaz boşluk karakterleri) için çalışmaz.
tzot

7

Exec komutunu kullanarak mem maliyetinden kaçınmak istiyorsanız, xargs ile daha iyisini yapabileceğinize inanıyorum. Bence aşağıdakiler daha etkili bir alternatif

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec



find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/

6

Bash olarak, alternatif shopt -s extglobbir GLOBIGNOREdeğişken . Gerçekten daha iyi değil ama hatırlamayı daha kolay buluyorum.

Orijinal posterin istediği bir örnek olabilir:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/

Bittiğinde , kaynak dizinde unset GLOBIGNOREyapabilmek rm *techno*.


5

Ayrıca oldukça basit bir fordöngü de kullanabilirsiniz :

for f in `find . -not -name "*Music*"`
do
    cp $f /target/dir
done

1
Bu, OP'nin istediklerinden farklı bir davranış olan özyinelemeli bir bulgu yapar.
Adam Rosenfield

1
-maxdepth 1özyinelemesiz için kullanın ?
avtomaton

Kabuk seçeneklerini etkinleştirmek / devre dışı bırakmak zorunda kalmadan bunu en temiz çözüm olarak buldum. OP'nin ihtiyaç duyduğu sonuca sahip olmak için bu yazıda -maxdepth seçeneği önerilebilir, ancak hepsi ne yapmaya çalıştığınıza bağlıdır.
David Lapointe

findÖnemsiz dosya adları bulursa, geri tıklamayla kullanmak hoş olmayan yollarla bozulur.
Üçlü

5

Kişisel tercihim grep ve while komutunu kullanmak. Bu, güçlü ve okunabilir komut dosyaları yazabilmenizi sağlar ve böylece tam olarak ne istediğinizi yaparsınız. Ayrıca bir yankı komutu kullanarak gerçek işlemi gerçekleştirmeden önce kuru bir çalışma gerçekleştirebilirsiniz. Örneğin:

ls | grep -v "Music" | while read filename
do
echo $filename
done

kopyalayacağınız dosyaları yazdırır. Liste doğruysa, bir sonraki adım echo komutunu aşağıdaki gibi copy komutuyla değiştirmektir:

ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done

1
Bu, dosya adlarınızda herhangi bir sekme, yeni satır, bir satırda birden fazla boşluk veya ters eğik çizgi olmadığı sürece çalışır. Bunlar patolojik vakalar olsa da, olasılığın farkında olmak iyidir. İçinde bashkullanabilirsiniz while IFS='' read -r filename, ancak o zaman yeni satırlar hala bir sorun. Genel olarak lsdosyaları numaralandırmak için kullanmamak en iyisidir ; gibi araçlar findçok daha uygundur.
Thedward

Herhangi bir ek alet olmadan:for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Thedward

mywiki.wooledge.org/ParsingLs , bundan kaçınmanız için bazı ek nedenler listeler.
Üçlü

5

Ben kullanmayan henüz burada görmedim Bir hile extglob, findveya grepiki dosya setleri gibi listeleri ve tedavi etmektir "diff" onları kullanarak comm:

comm -23 <(ls) <(ls *Music*)

commdifffazladan sarsıntısı olmadığı için tercih edilir .

Bu döner set 1 tüm unsurları lsolan, olmayan set 2'de de ls *Music*. Bu, düzgün çalışabilmesi için her iki kümenin de sıralanmış sırada olmasını gerektirir. lsGenişletme için sorun değil, ancak böyle bir şey kullanıyorsanız find, çağırdığınızdan emin olun sort.

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)

Potansiyel olarak yararlı.


1
Hariç tutmanın yararlarından biri, dizini ilk etapta geçmek değildir. Bu çözüm, biri hariç ve biri hariç olmak üzere iki alt dizinin geçişini yapar .
Mark Stosberg

Çok iyi bir nokta, @ MarkStosberg. Bu tekniğin bir yan yararı, gerçek bir dosyadan hariç tutmaları okuyabilmenize rağmen, örneğincomm -23 <(ls) exclude_these.list
James

3

Bunun için bir çözüm find ile bulunabilir.

$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt

Bulun birkaç seçeneği vardır, neleri eklediğiniz ve hariç tuttuğunuz konusunda oldukça spesifik olabilirsiniz.

Edit: Adam yorumlarda bunun özyinelemeli olduğunu kaydetti. mindepth ve maxdepth seçeneklerini bulma bunu kontrol etmede yararlı olabilir.


Bu, farklı davranış olan özyinelemeli bir kopya yapar. Ayrıca, her dosya için çok sayıda dosya için çok verimsiz olabilecek yeni bir süreç ortaya çıkarır.
Adam Rosenfield

Bir işlemi üretmenin maliyeti, her dosyayı kopyalamanın oluşturduğu tüm ES'lere kıyasla yaklaşık olarak sıfırdır. Bu yüzden, ara sıra kullanım için yeterince iyi olduğunu söyleyebilirim.
dland

Süreç yumurtlama için bazı çözümler: stackoverflow.com/questions/186099/…
Vinko Vrsalovic

özyinelemeyi önlemek için "-maxdepth 1" kullanın.
ejgottl

kabuk vahşi kart genişletme analog almak için backticks kullanın: cp find -maxdepth 1 -not -name '*Music*'/ target_directory
ejgottl

2

Aşağıdaki çalışmalarda, *.txtbir sayıyla başlayanlar dışında geçerli dizindeki tüm dosyalar listelenir .

Bu çalışır bash, dash, zshve diğer tüm POSIX uyumlu kabuklar.

for FILE in /some/dir/*.txt; do    # for each *.txt file
    case "${FILE##*/}" in          #   if file basename...
        [0-9]*) continue ;;        #   starts with digit: skip
    esac
    ## otherwise, do stuff with $FILE here
done
  1. Birinci satırda desen /some/dir/*.txt, fordöngünün /some/diradı biten tüm dosyalar üzerinde yinelenmesine neden olur .txt.

  2. İkinci satırda, istenmeyen dosyaları ayıklamak için bir vaka ifadesi kullanılır. - ${FILE##*/}İfade, dosya adındaki (burada /some/dir/) önde gelen dir adı bileşenlerini kaldırır, böylece patters yalnızca dosyanın adıyla eşleşebilir. (Dosya adlarını yalnızca soneklere göre ayıklıyoruz, $FILEbunun yerine kısaltabilirsiniz .)

  3. Üçüncü satırda, casekalıba uyan tüm dosyalar [0-9]*) satırı atlanır ( continueifade, fordöngünün bir sonraki yinelemesine atlar ). - İsterseniz burada daha ilginç bir şey yapabilirsiniz, örneğin kullanarak bir harfle (a – z) başlamayan tüm dosyaları atlamak gibi veya birkaç dosya adını [!a-z]*atlamak için birden fazla desen kullanabilirsiniz, örneğin [0-9]*|*.bakdosyaları her iki .bakdosyayı atlamak için ve bir sayıyla başlamayan dosyalar.


Doh! (Karşı *.txtyerine sadece karşı eşleşen) bir hata vardı *. Şimdi düzeltildi.
zrajm

0

bu tam olarak 'Müzik' hariç

cp -a ^'Music' /target

Bu ve Müzik? * veya *? Müzik gibi şeyleri hariç tutmak için

cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target

cpMacOS üzerindeki manuel sayfa bir sahiptir -aseçeneği ama tamamen farklı bir şey yok. Hangi platform bunu destekliyor?
Üçlü
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.