Her bir dizine nasıl gidip bir komut nasıl çalıştırılır?


144

Nasıl bir parent_directory içindeki her dizinde geçer ve bir bash betiği yazabilirim yürüten bir komut içinde her dizinde .

Dizin yapısı aşağıdaki gibidir:

parent_directory (ad herhangi bir şey olabilir - bir kalıp izlemez)

  • 001 (dizin adları bu modeli izler)
    • 0001.txt (dosya adları bu modeli izler)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

dizin sayısı bilinmiyor.

Yanıtlar:


108

Geçerli dizininiz aşağıdaki işlemleri gerçekleştirebilir parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

(Ve )geçerli dizin ana senaryoda değişmez böylece, bir altkabuk oluşturun.


5
Burada önemli değildir, çünkü joker karakter sayılardan başka bir şeyle eşleşmez, ancak genel durumda, dizin adını her zaman döngü içinde çift tırnak içine almak istersiniz. cd "$d"joker karakterin adları boşluk ve / veya kabuk metakarakterleri içeren dosyalarla eşleştiği durumlara aktarılması daha iyi olur.
tripleee

1
(Buradaki tüm cevaplar aynı kusura sahip gibi görünüyor, ancak en çok oy alan cevapta en önemli olanı.)
tripleee

1
Ayrıca, komutların büyük çoğunluğu onları hangi dizinde yürüttüğünüzü umursamaz. for f in foo bar; do cat "$f"/*.txt; done >outputişlevsel olarak eşdeğerdir cat foo/*.txt bar/*.txt >output. Ancak, lsbağımsız değişkenleri nasıl ilettiğinize bağlı olarak biraz farklı çıktılar üreten bir komuttur; benzer şekilde, bir alt dizinden çalıştırırsanız, göreli bir dosya adı veren a grepveya wchangisi farklı olur (ancak genellikle bu nedenle bir alt dizine girmekten kaçınmak istersiniz).
tripleee

159

Todd tarafından gönderilen bu cevap bana yardımcı oldu.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

\( ! -name . \) Geçerli dizinde komut yürütme önler.


4
Ve komutu sadece belirli klasörlerde çalıştırmak istiyorsanız:find FOLDER* -maxdepth 0 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;
Martin K

3
-exec bash -c 'cd "$0" && pwd' {} \;Bazı dizinler tek tırnak ve bazı çift tırnak içerdiğinden yapmak zorundaydım .
Charlie Gorichanaz

12
Ayrıca şu anki dizini ekleyerek atlayabilirsiniz-mindepth 1
mdziob

Bunu bir işlev olarak değiştirmek yerine pwddoğrudan "git remote get-url origin` gibi bir komutu kabul eder misiniz?
roachsinai

disd(){find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c 'cd "{}" && "$@"' \;}çalışmıyor.
roachsinai

48

GNU kullanıyorsanız find, -execdirparametreyi deneyebilirsiniz , örneğin:

find . -type d -execdir realpath "{}" ';'

veya ( @gniourf_gniourf yorumuna göre ):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

Not: Önde sabitlemek ${0#./}yerine kullanabilirsiniz .$0./

veya daha pratik bir örnek:

find . -name .git -type d -execdir git pull -v ';'

Geçerli dizini eklemek istiyorsanız, aşağıdakileri kullanarak daha da basittir -exec:

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

veya kullanarak xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Veya @gniourf_gniourf tarafından önerilen benzer bir örnek :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

Yukarıdaki örnekler adlarında boşluk bulunan dizinleri destekler.


Veya bash dizisine atayarak:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

Değişim .özel klasör adının. Yinelemeli olarak çalıştırmanız gerekmiyorsa, dirs=(*)bunun yerine şunu kullanabilirsiniz:. Yukarıdaki örnek, adında boşluk bulunan dizinleri desteklemez.

@Gniourf_gniourf'un önerdiği gibi, bulgu çıktısını açık bir döngü kullanmadan bir diziye koymanın tek uygun yolu Bash 4.4'te şu şekilde kullanılabilir:

mapfile -t -d '' dirs < <(find . -type d -print0)

Veya önerilen bir yol değil ( ayrıştırmayı içerirls ):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

Yukarıdaki örnek, (OP tarafından istendiği gibi) geçerli dizini yok sayar, ancak boşluklu isimleri keser.

Ayrıca bakınız:


1
findAçık bir döngü kullanmadan bir dizinin çıktısını koymanın tek uygun yolu ile Bash 4.4'te kullanılabilir mapfile -t -d '' dirs < <(find . -type d -print0). O zamana kadar, tek seçeneğiniz bir döngü kullanmak (veya işlemi ertelemek find).
gniourf_gniourf

1
Aslında, dirsdiziye gerçekten ihtiyacınız yok ; Eğer döngü olabilir findbireyin çıkışı (güvenle) şöyle: find . -type d -print0 | while IFS= read -r -d '' file; do ...; done.
gniourf_gniourf

@gniourf_gniourf Görselim ve her zaman basit görünen şeyleri bulmaya / yapmaya çalışıyorum. Örneğin ben bu script ve bash dizisi kullanarak benim için kolay ve okunabilir (mantık boruları kullanmak yerine, küçük parçalara ayırarak). Ek borular Tanıtımı, IFS, read, ek karmaşıklık izlenimi verir. Bunu düşünmem gerekecek, belki de bunu yapmanın daha kolay bir yolu var.
kenorb

Tarafından ise daha kolay sen demek kırık evet, sonra yapmanın daha kolay yolları vardır. Şimdi endişelenmeyin, tüm bunlar IFSve read(dediğiniz gibi) onlara alıştıkça ikinci bir doğa haline gelirler… onlar senaryo yazmanın kanonik ve deyimsel yollarının bir parçasıdır.
gniourf_gniourf

Bu arada, ilk komutunuz find . -type d -execdir echo $(pwd)/{} ';'istediğinizi yapmaz (daha $(pwd)önce genişletilir findbile yürütülür)…
gniourf_gniourf

29

Bunu borulama ve daha sonra kullanarak yapabilirsiniz xargs. Yakalama, -Ibash komutunuzdaki alt dizeyi, her birinin geçirdiği alt dizeyle değiştirecek bayrağı kullanmanızdır xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

pwdHer dizinde yürütmek istediğiniz komutla değiştirmek isteyebilirsiniz.


2
Bunun neden seçilmediğini bilmiyorum, ama şimdiye kadar bulduğum en basit komut dosyası. Teşekkürler
Linvi

20

Eğer üst düzey klasör biliniyorsa, şöyle bir şey yazabilirsiniz:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

$ (İstediğiniz kadar oynayın); istediğiniz kadar kod koyabilirsiniz.

Ben herhangi bir dizinde "cd" olmadığını unutmayın.

Alkış,


3
Ait örnek bir çift vardır "nin Yararsız Kullanımı ls" Burada - sadece yapabileceği for dir in $YOUR_TOP_LEVEL_FOLDER/*yerine.
Mark Longair

1
Belki de genel anlamda işe yaramaz, ancak doğrudan ls'de filtreleme yapılmasına izin verir (yani tüm dizinler .tmp ile sona erdi). Bu yüzden ls $dirifadeyi kullandım
gforcada

13
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done

Ve bunu anlamak çok kolay!
Rbjz

6

Bir astar hızlı ve kirli kullanım için iyi olsa da, komut dosyaları yazmak için daha ayrıntılı sürümü tercih ederim. Bu, birçok kenar vakasıyla ilgilenen ve bir klasörde yürütmek için daha karmaşık kod yazmanıza izin veren kullandığım şablon. Bash kodunuzu dir_command fonksiyonuna yazabilirsiniz. Aşağıda, dir_coomand git'deki her havuzu etiketlemek için örnek olarak uygulanmaktadır. Betiğin geri kalanı dizindeki her klasör için dir_command öğesini çağırır. Yalnızca verilen klasör kümesi üzerinden yineleme örneği de buna dahildir.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"

5

Dosyanın biçimlendirilmesiyle ilgili bir noktaya gelemiyorum, çünkü sadece klasörler arasında yineleme yapmak istiyorsunuz ... Böyle bir şey mi arıyorsunuz?

cd parent
find . -type d | while read d; do
   ls $d/
done

4

kullanabilirsiniz

find .

geçerli dizindeki tüm dosyaları / dizinleri arama

Daha xargs komutunun çıktısını bu şekilde aktarabilirsiniz

find . | xargs 'command here'

3
İstenen bu değil.
kenorb

0
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done

Dizini yeniden değiştirmeniz veya cdalt kabukta yapmanız gerekir .
Mark Longair

Downvote: düzenleme kaybetti, cdbu yüzden bu kod gerçekten yararlı bir şey yapmaz.
üç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.