Adlarında boşluk ve tırnak içeren dosyaları kopyalamak için xargs'ı nasıl kullanabilirim?


232

Bir dizin altındaki dosyaları bir grup kopyalamaya çalışıyorum ve dosyaların bir dizi adları ve tek tırnak adları var. Ben birlikte dize çalıştığınızda findve grepbirlikte xargs, aşağıdaki hatayı alıyorum:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

Daha sağlam xargs kullanımı için herhangi bir öneriniz var mı?

Bu, BSD'li Mac OS X 10.5.3'te (Leopard) xargs.


2
Bunun için GNU xargs hata mesajı tek bir alıntı içeren bir dosya adı ile oldukça yararlıdır: "xargs: eşsiz tek tırnak; -0 seçeneği kullanılmadıkça varsayılan olarak tırnaklar xargs için özeldir".
Steve Jessop

3
GNU xargs ayrıca --delimiter( -d) seçeneğine de sahiptir . \nSınırlayıcı olarak deneyin , Bu xargs, boşluklu satırları birkaç kelimeye / bağımsız değişkene ayırmayı önler .
MattBianco

Yanıtlar:


199

Tüm bunları tek bir findkomutta birleştirebilirsiniz:

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

Bu, içinde boşluk bulunan dosya adlarını ve dizinleri işleyecektir. Büyük / -nameküçük harfe duyarlı sonuçlar elde etmek için kullanabilirsiniz .

Not: Aktarılan --bayrak , seçenek olarak cpbaşlayan dosyaları işlemesini engeller -.


70
İnsanlar xargs kullanır, çünkü her seferinde 200 argümanla 5 kez yürütülebilir dosyayı çağırmak her seferinde bir argümanla 1000 kez çağırmaktan daha hızlıdır.
tzot

12
Chris Jester-Young'ın cevabı orada "iyi cevap" olmalı ... BTW bir dosya adı "-" ile başlıyorsa bu çözüm çalışmaz. En azından, cp sonra "-" gerekir.
Keltia

11
Hız örneği - 829 dosya üzerinde "find -exec" yöntemi 26 saniye sürerken "find -print0 | xargs --null" yöntem aracı 0,7 saniye sürdü. Önemli fark.
Peter Porter

7
@tzot Geç bir yorum ama yine de, xargstanımladığınız sorunu çözmek için gerekli değildir, findzaten -exec +noktalama işaretleriyle destekliyor .
jlliagre

3
boşluklarla nasıl başa çıkılacağı sorusuna cevap vermiyor
Ben Glasser

117

find . -print0 | grep --null 'FooBar' | xargs -0 ...

Leopard'da destek olup olmadığını ya da destekleyip grepdesteklemediğini bilmiyorum , ancak GNU'da her şey iyi.--nullxargs-0


1
Leopard "-Z" yi (GNU grep) destekler ve elbette (1) ve xargs (1) "-0" 'ı destekler.
Keltia

1
OS X grep -{z|Z}10.9'da "her dosya adından sonra sıfır bayt yazdır" değil, "zgrep gibi davran" (açma) anlamına gelir. grep --nullİkincisine ulaşmak için kullanın .
bassim

4
Sorun nedir find . -name 'FooBar' -print0 | xargs -0 ...?
Quentin Pradet

1
@QuentinPradet Açıkçası, "FooBar" gibi sabit bir dize için -nameya da -pathgayet iyi çalışıyor. OP grep, muhtemelen listeyi normal ifadeler kullanarak filtrelemek istedikleri için kullanımını belirtmiştir.
Chris Jester-Young

1
@ Adlı That Hi-Melek aynen kullandığım neden xargs -0 birlikte find -print0 . İkincisi, dosya adlarını bir NUL sonlandırıcıyla yazdırır ve birincisi dosyaları bu şekilde alır. Neden? Unix'teki dosya adları yeni satır karakterleri içerebilir. Ancak NUL karakterler içeremezler.
Chris Jester-Young

92

Orijinal posterin istediklerini yapmanın en kolay yolu, sınırlayıcıyı herhangi bir boşluktan sadece satır sonu karakterine dönüştürmektir:

find whatever ... | xargs -d "\n" cp -t /var/tmp

4
Bu anwser basit, etkili ve doğrudan nokta: xargs için ayarlanan varsayılan sınırlayıcı çok geniştir ve OP'nin yapmak istediği şey için daraltılması gerekir. Bunu ilk elden biliyorum, çünkü bugün aynı konuya cygwin dışında benzer bir şey yaptım. Xargs komutunun yardımını okumuş olsaydım, birkaç baş ağrısından kaçınmış olabilirdim, ancak çözümünüz benim için düzeltti. Teşekkürler ! (Evet, OP, ben kullanmadığım BSD xargs kullanarak MacOS vardı, ama xargs "-d" parametresinin tüm sürümlerinde var umut ediyorum).
Etienne Delavennat

7
Güzel cevap ama Mac üzerinde çalışmıyor. Bunun yerine boru can find içine sed -e 's_\(.*\)_"\1"_g'dosya adı etrafında kuvvet tırnak için
ishahak

10
Bu kabul edilen cevap olmalı. Soru kullanmaktı xargs.
Mohammad Alhashash

2
Ben olsunxargs: illegal option -- d
nehem

1
Dosya adlarının birçok * nix sisteminde yeni satır karakteri içerebileceğini belirtmek gerekir. Vahşi doğada bununla karşılaşmanız pek olası değildir, ancak güvenilmeyen girdide kabuk komutları çalıştırıyorsanız bu bir endişe kaynağı olabilir.
Soren Bjornstad

71

Bu "cp" yi birden fazla çalıştırmadığından daha etkilidir:

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar

1
Bu benim için işe yaramadı. Ne bulursanız olun içine cp ~ / foo / bar denemeye çalıştı, ama tam tersi değil
Shervin Asgari

13
Cp'nin -t bayrağı bir GNU uzantısıdır, AFAIK ve OS X'te kullanılamaz. Ancak, öyleyse bu yanıtta gösterildiği gibi çalışır.
metamatt

2
Linux kullanıyorum. '-T' anahtarı için teşekkürler. Ben eksik olan bu :-)
Vahid Pazirandeh

59

Ben de aynı problemle karşılaştım. İşte nasıl çözdüm:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

Kullandığım sedAynı hat ile girişin her satırı yerine, ancak çift tırnak içine. Gönderen sedadam sayfasında, " ... An işareti (` `& '') değiştirme görünen RE ... eşleşen dize ile değiştirilir " - bu durumda,.* , tüm hattı.

Bu xargs: unterminated quotehatayı çözer .


3
Windows ve gnuwin32 kullanıyorum, bu yüzden sed s/.*/\"&\"/işe almak için kullanmak zorunda kaldı .
Pat

Evet, ancak "sed de tırnak işaretleri içinde olmadıkça , bu muhtemelen in ile dosya adlarını işlemez mi?
artfulrobot

Kullanımı seddeha ve şimdilik sorunu yeniden yazmadan doğru çözüm!
entonio

53

Bu yöntem Mac OS Xv10.7.5 (Lion) üzerinde çalışır :

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

Gönderdiğiniz sözdizimini de test ettim. Bu da 10.7.5'te işe yaradı.


4
Bu işe yarar, ancak -Iima eder -L 1(elkitabını söyler), bu da cp komutunun dosya başına bir kez çalıştırıldığı anlamına gelir = v yavaş.
artfulrobot

xargs -J% cp% <destination dir> OSX'de muhtemelen daha verimlidir.
Walker D

3
Üzgünüm, ama bu YANLIŞ. İlk olarak TO kaçınmak istediği hatayı üretir. Xargs'ın "varsayılan tırnaklar özeldir" etrafında find ... -print0ve xargs -0çalışmak için kullanmalısınız . İkincisi, genellikle kullanmak '{}'değil {}boşluk ve özel karakterler karşı korumak için, Xargs geçirilen komutları.
Andreas Spindler

3
Üzgünüm Andreas Spindler, xargs'a aşina değilim ve bazı deneylerden sonra bu çizgiyi buldum. Bu konuda yorum yapan ve iptal eden çoğu insan için işe yarıyor gibi görünüyor. Ne tür bir hata ürettiği hakkında biraz daha ayrıntıya girmeyi düşünür müsünüz? Ayrıca, daha doğru olacağını düşündüğünüz girdiyi göndermenizi ister misiniz? Teşekkür ederim.
the_minted

12

Sadece kullanma xargs. Düzgün bir program ama iyi gitmiyorfind önemsiz vakalarla karşı karşıya kaldığında .

İşte taşınabilir (POSIX) çözümü, yani gerektirmez biridir find, xargsya da cpGNU belirli uzantıları:

find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +

+Daha olağan yerine sonuna dikkat edin; .

Bu çözüm:

  • gömülü boşluklar, yeni satırlar veya herhangi bir egzotik karakter içeren dosyaları ve dizinleri doğru şekilde işler.

  • GNU araç seti sağlamayanlar bile herhangi bir Unix ve Linux sisteminde çalışır.

  • kullanmayan xargsgüzel ve kullanışlı bir program, ancak düzgün işlemek için çok fazla ince ayar ve standart olmayan özellikler gerektiren findçıktı.

  • aynı zamanda kabul edilenlerden daha verimli ( daha hızlı okunur ) ve diğer yanıtların tümü olmasa da çoğu.

Ayrıca, diğer bazı cevaplarda veya alıntılarda belirtilenlere rağmen {} yapmak (egzotik fishkabuğu kullanmıyorsanız ) işe yaramaz .



1
@PeterMortensen Muhtemelen bitiş artısını görmezden geliyorsunuz. herhangi bir ek yük olmadan findne yapabilirim xargs.
jlliagre

8

Bulma sırasında -print0 seçeneğiyle xargs için --null komut satırı seçeneğini kullanmaya bakın.


8

Bulmak dışında komutlara güvenenler için, örneğin ls:

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar

1
Çalışıyor ama yavaş çünkü -Iima ediyor-L 1
artfulrobot

6
find | perl -lne 'print quotemeta' | xargs ls -d

Bunun satır besleme dışında herhangi bir karakter için güvenilir bir şekilde çalışacağına inanıyorum (ve dosya adlarınızda satır beslemeniz varsa, bundan daha kötü sorunlarınız olduğundan şüpheleniyorum). GNU bulucuları gerektirmez, sadece Perl'dir, bu yüzden hemen hemen her yerde çalışmalıdır.


Dosya adında satır beslemesi olması mümkün müdür? Hiç duymadım.
mtk

2
Gerçekten öyle. Örneğin, deneyinmkdir test && cd test && perl -e 'open $fh, ">", "this-file-contains-a-\n-here"' && ls | od -tx1
mavit

1
|perl -lne 'print quotemeta'tam olarak aradığım şey. Buradaki diğer gönderiler bana yardımcı olmadı çünkü PHP dosyalarının sayısını sadece kötü amaçlı yazılım bulaşmış olanlara azaltmak findiçin kullanmam gerekti grep -rl.
Marcos

perl ve quotemeta print0 / -0'dan çok daha geneldir
boşluklu boru

5

Aşağıdaki sözdiziminin benim için iyi çalıştığını gördüm.

find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200

Bu örnekte, "/ usr / pcapps" dosyasına monte edilen dosya sisteminde 1.000.000 baytın üzerindeki en büyük 200 dosyayı arıyorum.

"Bul" ve "xargs" arasındaki Perl satır astarı, her bir boşluktan kaçar / tırnaklar, böylece "xargs", gömülü boşlukları olan herhangi bir dosya adını "ls" ye tek bir argüman olarak iletir.


3

Çerçeve zorluğu - xargs'ın nasıl kullanılacağını soruyorsunuz. Cevap: xargs kullanmıyorsunuz çünkü buna ihtiyacınız yok.

Tarafındanuser80168 yapılan yorum , her dosya için cp çağırmadan, bunu doğrudan cp ile yapmanın bir yolunu açıklar:

find . -name '*FooBar*' -exec cp -t /tmp -- {} +

Bu çalışır çünkü:

  • cp -tbayrak başlangıcına yakın hedef dizini yapmamızı sağlar cpziyade sonuna yakın daha. Gönderen man cp:
   -t, --target-directory=DIRECTORY
         copy all SOURCE arguments into DIRECTORY
  • --Bayrak söyler cpile başlayan dosyalar böylece, bir dosya adı değil, bir bayrak olarak sonra her şeyi yorumlamak -veya --karıştırmayın cp; buna hala ihtiyacınız var çünkü -/ --karakterleri tarafından yorumlanırken cp, diğer özel karakterler kabuk tarafından yorumlanır.

  • find -exec command {} +Değişik biçim esasen Xargs aynı yapar. Gönderen man find:

   -exec command {} +                                                     
         This  variant  of the -exec action runs the specified command on
         the selected files, but the command line is built  by  appending
         each  selected file name at the end; the total number of invoca‐
         matched  files.   The command line is built in much the same way
         that xargs builds its command lines.  Only one instance of  `{}'
         is  allowed  within the command, and (when find is being invoked
         from a shell) it should be quoted (for example, '{}') to protect
         it  from  interpretation  by shells.  The command is executed in
         the starting directory.  If any invocation  returns  a  non-zero
         value  as exit status, then find returns a non-zero exit status.
         If find encounters an error, this can sometimes cause an immedi‐
         ate  exit, so some pending commands may not be run at all.  This
         variant of -exec always returns true.

Bunu doğrudan bulmak için kullanarak, dosya adlarındaki kötü karakterler hakkında endişelenmenize gerek kalmayacak şekilde bir boru veya kabuk çağırma ihtiyacını ortadan kaldırır.


Şaşırtıcı bulmak, hiçbir fikrim yoktu !!! "-exec yardımcı programı [argüman ...] {} + -exec ile aynı, ancak` `{} '' yerine her yardımcı program çağrısı için olabildiğince çok yol adı girildi. Bu davranış xargs (1) ile aynı. )." BSD uygulamasında.
conny

2

Diğer yanıtlarda tartışılan seçeneklerin çoğunun GNU yardımcı programlarını kullanmayan platformlarda (örneğin Solaris, AIX, HP-UX) standart olmadığını unutmayın. POSIX'e bakın'Standart' xargs davranışı için spesifikasyonuna .

Ayrıca xargs'ın davranışını en az bir kez, hiç girdi olmadan bile, bir rahatsızlık olarak çalıştırdığı şekilde bulurum.

İsimlerdeki boşlukların sorunlarıyla başa çıkmak için kendi özel xargs (xargl) sürümümü yazdım (sadece yeni satırlar ayrı - 'find ... -print0' ve 'xargs -0' kombinasyonu dosya adlarının yapılamadığı göz önüne alındığında oldukça düzgün ASCII NUL '\ 0' karakterleri içeriyor.


2
GitHub ya da olmadı
Corey Goldberg

@CoreyGoldberg: Sanırım o zaman olmadı.
Jonathan Leffler

POSIX'in ilk etapta findihtiyacı yoktur xargs(ve bu 11 yıl önce doğruydu).
jlliagre

2

Bash ile (POSIX değil), geçerli satırı bir değişkenin içine almak için işlem ikamesini kullanabilirsiniz. Bu, özel karakterlerden kaçmak için tırnak işaretlerini kullanmanızı sağlar:

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)

2

Benim için biraz farklı bir şeyler yapmaya çalışıyordum. .Txt dosyalarımı tmp klasörüme kopyalamak istedim. .Txt dosya adları boşluklar ve kesme işareti karakterleri içerir. Bu benim Mac bilgisayarımda çalıştı.

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/

1

Sisteminizdeki find ve xarg sürümleri desteklemiyorsa -print0ve -0anahtarları (örneğin AIX find ve xargs) bu korkunç görünümlü kodu kullanabilirsiniz:

 find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest

Burada sed, xargs için boşluklardan ve tırnaklardan kaçmaya özen gösterecektir.

AIX 5.3 üzerinde test edilmiştir


1

Sorunların çoğunu ele alan "xargs" etrafında "xargsL" adlı küçük bir taşınabilir sarmalayıcı komut dosyası oluşturdum.

XargsL'nin aksine, xargsL her satıra bir yol adı kabul eder. Yol adları (açıkça) yeni satır veya NUL bayt dışında herhangi bir karakter içerebilir.

Dosya listesinde alıntı yapılmasına izin verilmez veya desteklenmez - dosya adlarınız her türlü boşluk, ters eğik çizgi, ters tırnak, kabuk joker karakterleri ve benzerlerini içerebilir - xargsL bunları değişmez karakterler olarak işleyecektir, zarar verilmez.

Bir avantaj özelliği olarak, xargsL olacak değil hiçbir giriş olmazsa bir kez komutunu çalıştırın!

Farkı not edin:

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

XargsL öğesine verilen tüm argümanlar xargs'e iletilecektir.

İşte "xargsL" POSIX kabuk betiği:

#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.

set -e
trap 'test $? = 0 || echo "$0 failed!" >& 2' 0

if IFS= read -r first
then
        {
                printf '%s\n' "$first"
                cat
        } | sed 's/./\\&/g' | xargs ${1+"$@"}
fi

Komut dosyasını $ PATH içindeki bir dizine koyun ve unutmayın

$ chmod +x xargsL

orada çalıştırılabilir hale getirmek için komut dosyası.


1

bill_starr'ın Perl sürümü gömülü yeni satırlar için iyi çalışmaz (yalnızca boşluklu başlıklar ). Örneğin Solaris'te GNU araçlarına sahip olmayanlar için daha eksiksiz bir sürüm (sed kullanarak) olabilir ...

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

find ve grep argümanlarını veya diğer komutları istediğiniz gibi ayarlayın, ancak sed gömülü yeni satırlarınızı / boşluklarınızı / sekmelerinizi düzeltir.


1

Ben kullanılan Bill Star'ın cevabı biraz Solaris değişiklik:

find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file

Bu, her satıra tırnak işareti koyacaktır. Her ne kadar muhtemelen yardımcı olur '-l' seçeneğini kullanmıyordu.

Yine de gittiğim dosya listesinde '-' olabilir, ancak yeni satırlar olmayabilir. Çıkış dosyasını başka komutlarla birlikte kullanmadım, çünkü bunları xargs aracılığıyla büyük ölçüde silmeye başlamadan önce bulunanları gözden geçirmek istiyorum.


1

Bununla biraz oynadım, xargs değiştirmeyi düşünmeye başladım ve burada bahsettiğimiz türden bir kullanım durumu için Python'da basit bir yeniden uygulamanın daha iyi bir fikir olduğunu fark ettim.

Bir şey için, her şey için ~ 80 satır kod olması, neler olduğunu anlamanın kolay olduğu anlamına gelir ve farklı davranışlar gerekiyorsa, yeni bir komut dosyasına almak için gerekenden daha kısa sürede hackleyebilirsiniz Stack Overflow gibi bir yerde cevap.

Bkz. Https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs ve https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py .

Yazılı iplikler (ve Python 3 yüklü) ile şunları yazabilirsiniz:

find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar

aynı anda 203 dosyayı kopyalamak için. (Burada 203 elbette sadece bir yer tutucudur ve 203 gibi garip bir sayı kullanmak, bu sayının başka bir önemi olmadığını açıkça ortaya koymaktadır.)

Gerçekten daha hızlı ve Python'a ihtiyaç duymadan bir şey istiyorsanız, zargerleri ve ipleri prototip olarak alın ve C ++ veya C'de yeniden yazın.


0

Foobar dizinini aşağıdaki gibi grep gerekebilir:

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .

1
Man sayfası başına -i, kullanımdan kaldırılmıştır ve -Ibunun yerine kullanılmalıdır.
Acumenus

-1

Bash kullanıyorsanız, stdout'u bir satır dizisine şu şekilde dönüştürebilirsiniz mapfile:

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

Avantajları:

  • Yerleşik, bu yüzden daha hızlı.
  • Komutu tüm dosya adlarıyla tek seferde yürütün, böylece daha hızlı olur.
  • Dosya adlarına başka bağımsız değişkenler ekleyebilirsiniz. İçin cpaşağıdakileri de yapabilirsiniz:

    find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
    

    ancak, bazı komutların böyle bir özelliği yoktur.

Dezavantajlar:

  • Çok fazla dosya adı varsa iyi ölçeklendirilmeyebilir. (Sınır? Bilmiyorum, ama Debian altında 10000+ dosya adı içeren 10 MB liste dosyası ile test ettim)

Peki ... Bash'in OS X'te mevcut olup olmadığını kim bilebilir?

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.