Bir dizindeki tüm dosyalarda komut yürütme


291

Birisi lütfen aşağıdakileri yapmak için kodu sağlayabilir mi: Hepsinin bir program aracılığıyla çalıştırılması gereken bir dosya dizini olduğunu varsayın. Program sonuçları standart çıktıya verir. Bir dizine gitmek, her dosyada komutu yürütmek ve bir büyük çıkış dosyasına çıktı concat olacak bir komut dosyası gerekir.

Örneğin, komutu 1 dosyada çalıştırmak için:

$ cmd [option] [filename] > results.out

3
Soruya eklemek istiyorum. Xargs kullanılarak yapılabilir mi? ör., ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
Ozair Kafray

2
Olabilir, ancak muhtemelen sürmek için kullanmak istemezsinizlsxargs . Eğer cmdyetkin yazılı bütün olan, belki de sadece yapabilirsiniz cmd <wildcard>.
Üçlü

Yanıtlar:


426

Aşağıdaki bash kodu $ file komutunu iletir; burada $ file / dir içindeki her dosyayı temsil eder

for file in /dir/*
do
  cmd [option] "$file" >> results.out
done

Misal

el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt

23
İçinde hiçbir dosya yoksa /dir/, döngü hala bir kez '*' değeriyle çalışır $fileve bu istenmeyen bir durumdur. Bunu önlemek için, döngü süresi boyunca nullglob'u etkinleştirin. Bu satırı döngüden önce shopt -s nullglobve bu satırı döngüden sonra ekleyin shopt -u nullglob #revert nullglob back to it's normal default state.
Stew-au

43
+1, Ve sadece bana tüm duvar kağıdı koleksiyonuma mal oldu. benden sonra herkes çift tırnak kullanın. "$ file"
Behrooz

Çıktı dosyası döngünün içinde aynıysa, döngünün dışına yeniden yönlendirmek çok daha verimlidir done >results.out(ve muhtemelen burada varsaydığım gibi ekleme yerine üzerine yazabilirsiniz).
Üçlü

Giriş dosyalarına özel olarak adlandırılmış bireysel sonuç dosyalarını nasıl elde edersiniz?
Timothy Swan

1
dir büyük dosyaları için bu komutu kullanarak dikkatli olun. Bunun yerine find -exec öğesini kullanın.
kolisko

183

Buna ne dersin:

find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
  • -maxdepth 1argüman bulmanın herhangi bir alt dizine yinelemeli olarak inmesini engeller. (Bu tür iç içe geçmiş dizinlerin işlenmesini istiyorsanız, bunu atlayabilirsiniz.)
  • -type -f yalnızca düz dosyaların işleneceğini belirtir.
  • -exec cmd option {}bulduğu her dosya cmdiçin belirtilen optiondosya adıyla değiştirildiğini söyler{}
  • \; komutun sonunu belirtir.
  • Son olarak, tüm tek tek cmdyürütmelerden elde edilen çıktıresults.out

Ancak, dosyaların işlenme sırasını önemsiyorsanız, bir döngü yazmaktan daha iyi olabilirsiniz. Bence find(ben bu konuda yanlış olabilir) inode sırayla dosyaları işler, bu ne istediğinizi olmayabilir.


1
Dosyaları işlemenin doğru yolu budur. For döngüsü kullanmak birçok nedenden dolayı hataya açıktır. Ayrıca sıralama, tabii ki sıralama ölçütlerine bağlı olan statve gibi diğer komutlar kullanılarak da yapılabilir sort.
tuxdna

1
iki komut çalıştırmak istersem, -execseçeneklerden sonra bunları nasıl bağlarım? Onları tek tırnak içine almak zorunda mıyım?
frei

findher zaman en iyi seçenektir çünkü seçenekle dosya adı desenine göre filtreleyebilir -nameve tek bir komutta yapabilirsiniz.
João Pimentel Ferreira

3
@ frei sorunuzun cevabı burada: stackoverflow.com/a/6043896/1243247 ama temelde sadece -execseçenekleri ekleyin :find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
João Pimentel Ferreira

2
seçenek olarak dosya adına nasıl başvurabilirsiniz?
Toskan

55

Bunu komut satırından ahududu pi üzerinde çalıştırarak yapıyorum:

for i in *;do omxplayer "$i";done

7

Kabul edilen / yüksek oylanan cevaplar harika, ancak birkaç cesur ayrıntıdan yoksunlar. Bu yazı, dosya adları gömülü yeni satırlar / tire simgeleri içerdiğinde ve sonuçları bir dosya.

Kabuk glob genişletmesini kullanarak , dizinde hiçbir dosya *yoksa ve istenmeyen sonuçlara neden olabilecek çalıştırılacak komuta genişletilmemiş bir glob dizesi geçirilirse genişletmenin başarısız olma olasılığı vardır . Kabuk bu kullanmak için genişletilmiş bir kabuk seçeneği sağlar . Böylece döngü temelde dosyalarınızı içeren dizinin içinde aşağıdaki gibi olurbashnullglob

 shopt -s nullglob

 for file in ./*; do
     cmdToRun [option] -- "$file"
 done

Bu, ifade ./*herhangi bir dosya döndürmediğinde (dizin boşsa) for döngüsünden güvenle çıkmanızı sağlar.

veya POSIX uyumlu bir şekilde ( nullglobbir bashspesifik)

 for file in ./*; do
     [ -f "$file" ] || continue
     cmdToRun [option] -- "$file"
 done

Bu, ifade bir kez başarısız olduğunda döngüye girmenizi sağlar ve koşul [ -f "$file" ], genişletilmemiş dizenin ./*söz konusu dizinde geçerli olmayacak geçerli bir dosya adı olup olmadığını kontrol eder . Bu durumda başarısızlık durumunda, daha sonra çalışmayacak olan döngüye continuegeri dönüyoruz for.

Ayrıca --dosya adı bağımsız değişkenini geçmeden hemen önce kullanıldığına dikkat edin . Bu gereklidir, çünkü daha önce belirtildiği gibi, kabuk dosya adları dosya adının herhangi bir yerinde tire içerebilir. Kabuk komutlarının bazıları bunu yorumlar ve ad düzgün bir şekilde tırnak içine alınmadığında bir komut seçeneği olarak görür ve bayrak sağlanırsa komut düşüncesini yürütür.

Bu --durumda komut satırı seçeneklerinin sonuna işaret eder, yani komut bu noktanın ötesinde herhangi bir dizeyi komut bayrağı olarak ayrıştırmamalı, yalnızca dosya adları olarak ayrılmalıdır.


Dosya adlarının iki kez tırnak içine alınması, isimler glob karakterler veya boşluklar içerdiğinde durumları düzgün şekilde çözer. Ancak * nix dosya adları bunlarda yeni satırlar da içerebilir. Dolayısıyla, dosya adlarını geçerli bir dosya adının parçası olamayacak tek karakterle sınırlandırıyoruz - null byte ( \0). Yana bashdahili olarak kullandığı Cboş bayt dize sonunu belirtmek için kullanıldığı tarzı dizeleri, bunun için doğru aday.

Bu nedenle , komut seçeneğini printfkullanarak bu NULL bayt ile dosyaları sınırlamak için kabuk seçeneğini kullanarak, aşağıda yapabiliriz-dread

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done

Ve nullglobve printfetrafları sarılır , bu da komutun bir kez çıkmasından sonra ana kabuğa yansıtma seçeneğinden (..)kaçınmak için temel olarak bir alt kabukta (alt kabuk) çalıştırıldıkları anlamına gelir nullglob. -d ''Seçeneği readkomuta olduğunu değil yani ihtiyacı POSIX uyumlu bashbunun yapılması gerek için kabuk. Kullanımı findbu kadar yapılabilir komut

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)

İçin finddesteklemeyen uygulamalarda -print0(GNU ve FreeBSD uygulamaları hariç), bu kullanılarak taklit edilebilirprintf

find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --

Diğer önemli bir düzeltme, çok sayıda dosya G / Ç'sini azaltmak için yeniden yönü for-loop'un dışına taşımaktır. Döngü içinde kullanıldığında, kabuk for-loop'un her yinelemesi için sistem çağrılarını iki kez, bir kez açılmak ve bir kez dosya ile ilişkili dosya tanımlayıcıyı kapatmak için yürütmek zorundadır. Bu, büyük iterasyonlar yürütme performansınızda bir şişe boynu haline gelecektir. Önerilen öneri onu döngü dışına taşımak olacaktır.

Yukarıdaki kodu bu düzeltmelerle genişleterek şunları yapabilirsiniz:

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done > results.out

temel olarak dosya girişinizin her yinelemesi için komutunuzun içeriğini stdout'a koyar ve döngü sona erdiğinde, stdout'un içeriğini yazmak ve kaydetmek için hedef dosyayı bir kez açın. Aynı eşdeğeri findsürümü

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out

1
Dosyanın var olup olmadığını kontrol etmek için +1. Varolmayan bir dizinde arama yapılıyorsa, $ dosyası geçerli bir dosya adı değil "/ invald_dir / *" normal ifade dizesini içeriyor.
cdalxndr

3

Bazen işi bitiren hızlı ve kirli bir yol:

find directory/ | xargs  Command 

Örneğin, geçerli dizindeki tüm dosyalarda satır sayısını bulmak için şunları yapabilirsiniz:

find . | xargs wc -l

8
@Hubert Dosya adlarınızda neden yeni satırlar var ?!
musicin3d

2
"neden" sorusu değil, doğruluk meselesi - dosya adlarının yazdırılabilir karakterler içermesi gerekmez, geçerli UTF-8 dizileri bile olması gerekmez. Ayrıca, bir yeni satır çok kodlamaya bağımlıdır, bir kodlama another diğerinin yeni satırdır. Bkz. Kod sayfası 437
Hubert Kario

2
hadi, gerçekten mi? Bu zamanın% 99,9'u işe yarıyor ve "hızlı ve kirli" dedi
Edoardo

Ben "hızlı ve kirli" (AKA "kırık") hayranıyım Bash betikler. Er ya da geç ünlü "Taşındı ~/.local/share/steam. Buhar koştu. Kullanıcının sahip olduğu sistemdeki her şeyi sildi." hata raporu.
etkinliğini azaltma

Bu, adında boşluk bulunan dosyalarla da çalışmaz.
Shamas S -

2

Tüm .md dosyalarını bir dizinden diğerine kopyalamam gerekiyordu, işte burada yaptım.

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

Okuması oldukça zor, hadi onu parçalayalım.

ilk cd dosyalarının bulunduğu dizine

for i in **/*.md; deseninizdeki her dosya için

mkdir -p ../docs/"$i"bu dizini dosyalarınızı içeren klasörün dışındaki bir docs klasöründe yapın. Hangi dosya ile aynı ada sahip ekstra bir klasör oluşturur.

rm -r ../docs/"$i" sonucunda oluşturulan ekstra klasörü kaldır mkdir -p

cp "$i" "../docs/$i" Asıl dosyayı kopyala

echo "$i -> ../docs/$i" Ne yapıyorsun

; done Sonsuza dek mutlu yaşa


Not: **Çalışmak için globstarkabuk seçeneğinin ayarlanması gerekir:shopt -s globstar
Hubert Kario

2

Kullanabilirsiniz xarg

ls | xargs -L 1 -d '\n' your-desired-command

-L 1 nedenleri bir seferde 1 öğeyi geçmek

-d '\n'yapmak çıkış lsyeni satır dayalı bölünmüş '.


1

@Jim Lewis'in yaklaşımına dayanarak:

İşte finddosyaları değiştirme tarihlerine göre sıralayan ve sıralayan hızlı bir çözüm :

$ find  directory/ -maxdepth 1 -type f -print0 | \
  xargs -r0 stat -c "%y %n" | \
  sort | cut -d' ' -f4- | \
  xargs -d "\n" -I{} cmd -op1 {} 

Sıralama için bakınız:

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time


adlarında yeni satırlar varsa bu çalışmaz
Hubert Kario

1
Daha hakkında okumak isteyebilirsiniz @HubertKario -print0için findve -0için xargshangi yerine (satırbaşıyla dahil) boşluk sıfır karakteri kullanın.
tuxdna

evet, kullanmak -print0yardımcı olan bir şeydir, ancak tüm boru hattının böyle bir şey kullanması gerekir ve sortdeğil
Hubert Kario

1

basit çözüm olduğunu düşünüyorum:

sh /dir/* > ./result.txt

2
Soruyu doğru anladınız mı? Bu sadece dizindeki her dosyayı bir komut dosyası gibi kabuk üzerinden çalıştırmayı dener.
rdas

1

Maksimum derinlik

Jim Lewis'in cevabı ile güzel bir şekilde çalıştığını gördüm :

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out

Sıralama düzeni

Sıralama düzeninde yürütmek istiyorsanız, şu şekilde değiştirin:

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out

Sadece bir örnek için, bu aşağıdaki sırayla yürütülür:

bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh

Sınırsız Derinlik

Belirli bir koşulla sınırsız derinlikte yürütmek istiyorsanız, bunu kullanabilirsiniz:

export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out

daha sonra aşağıdaki gibi alt dizinlerdeki her dosyanın üstüne koyun:

#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return

ve üst dosyanın gövdesinde bir yerde:

if <a condition is matched>
then
    #execute child files
    export DIR=`pwd`
fi
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.