ls `ile döngü bash kabuk betiği ile sonuçlanır


93

Herhangi biri ls, dizin adları listesi için bir şey yapmak ve her birini döngüden geçirmek ve bir şeyler yapmak için bir şablon kabuk betiğine sahip mi?

ls -1d */Dizin adlarının listesini almak için yapmayı planlıyorum .

Yanıtlar:


109

@ Shawn-j-goff ve diğerlerinin önerdiği gibi, bir kürenin yapacağı yerde kullanılamaz.

Sadece bir for..do..donedöngü kullanın :

for f in *; do
  echo "File -> $f"
done

Sen değiştirebilirsiniz *ile *.txtya da (bu konuda dosya, dizin, ya da bir şey) bir listesini döndürür başka bir topak, bir liste üreten bir komut, örneğin $(cat filelist.txt), ya da aslında bir liste ile değiştirin.

doDöngü içinde sadece dolar işareti önekine sahip döngü değişkenine bakın ( $fyukarıdaki örnekte olduğu gibi). Yapabilirsin echoya da istediğin başka bir şey yapabilirsin.

Örneğin .xml, geçerli dizindeki tüm dosyaları yeniden adlandırmak için .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

Ya da daha iyisi, eğer Bash kullanıyorsanız, alt kabuk oluşturmak yerine Bash parametre genişletmelerini kullanabilirsiniz:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done

22
Ya dosya adında bir boşluk varsa?
Daniel A. White

4
Ne yazık ki, Daniel'in dediği gibi, bu cevaptaki kod, dosya veya klasörlerden herhangi birinin adında boşluk veya yeni satır içeriyorsa bozulur. forDöngülerin çok suiistimal edildiğini ve çıktısını ayrıştırmaya çalışan tipik bir tuzak olduğunu gösterir ls. @ DanielA.White, sen belki o yardımcı olmadı (veya potansiyel yanıltıcı) eğer gibi söylediğinden beri, dizinler göre hareket ediyoruz, bu cevabı unaccepting düşünün. Shawn J. Goff'un cevabı, sorununuza daha sağlam ve çalışan bir çözüm sağlamalıdır.
Ocak'ta

4
-∞ Çıktıyı çözümlememelisinizls , çıktıyı bir fordöngüde okumamalısınız , $()`` yerine kullanmalı ve More Quotes ™ kullanmalısınız . @CoverosGene'in iyi niyetli olduğundan eminim, ama bu çok korkunç.
l0b0

1
Gerçekten kullanmak istiyorsanız daha iyi bir alternatif lsolduğunu ls -1 | while read line; do stuff; done. En azından bu bir boşluk için kırılmayacak.
Emmanuel Joubaud

1
ile mv -vİhtiyacınız olmayanecho "moved $x -> $t"
DmitrySandalov

68

lsDosya adlarını almak için çıktısını kullanmak kötü bir fikirdir . Arıza ve hatta tehlikeli senaryolara yol açabilir. Bunun nedeni, bir dosya adının karakter /ve karakter dışında herhangi bir karakter içermesi ve bu nullkarakterlerden herhangi lsbirini sınırlayıcı olarak kullanmamasıdır, bu nedenle bir dosya adında boşluk veya yeni satır varsa , beklenmeyen sonuçlar elde edersiniz.

Dosyalar üzerinde yineleme yapmanın çok iyi iki yolu vardır. Burada basitçe echodosya adıyla bir şeyler yaptığımı göstermek için kullandım ; Yine de her şeyi kullanabilirsiniz.

İlki, kabuğun doğal globbing özelliklerini kullanmaktır.

for dir in */; do
  echo "$dir"
done

Kabuk */, fordöngünün okuduğu ayrı argümanlara genişler ; dosya foradında boşluk, satırsonu veya başka herhangi bir garip karakter olsa bile, her bir tam adı bir atom birimi olarak görecek; Listeyi hiçbir şekilde ayrıştırmıyor.

Eğer alt dizinleri içine yinelemeli gitmek istiyorsanız sizin kabuk gibi bazı genişletilmiş globbing özelliklere (olmadıkça, o zaman bu yapmayacağım bashs' globstarKabuk bu özelliklere sahip değilse. Veya sağlamak istiyorsanız sizin komut çalışacağını çeşitli sistemlerde, bir sonraki seçenek kullanmaktır find.

find . -type d -exec echo '{}' \;

Burada, findkomut arayacak echove dosya isminin bir argümanını iletecektir. Bu bulduğu her dosya için bir kez yapar. Önceki örnekte olduğu gibi, dosya listesinin ayrıştırılması yoktur; bunun yerine, bir dosya tamamen bağımsız değişken olarak gönderilir.

-execArgümanın sözdizimi biraz komik görünüyor. findilk argümanı sonra alır ve -execçalışacak program olarak ve sonraki her argümanda, o programa geçmek için argüman olarak kabul eder. Görmesi -execgereken iki özel argüman var . İlki {}; bu argüman, önceki bölümlerin findoluşturduğu bir dosya adı ile değiştirilir . İkincisi ;, bunun findprograma iletilecek olan argümanlar listesinin sonu olduğunu bilmesini sağlayan ; findBuna ihtiyacı var çünkü findexec'd programı için amaçlanan ve amaçlanmayan daha fazla argümanla devam edebilirsiniz . Bunun nedeni \, kabuğun da davranmasıdır.;özel olarak - bir komutun sonunu temsil eder, bu yüzden ondan kaçmamız gerekir, böylece kabuk findonu kendisi için tüketmekten ziyade bir argüman olarak verir ; özel olarak işlem görmemesi için kabuğun elde edilmesinin bir başka yolu tırnak içine almaktır: bu amaçla ';'olduğu gibi çalışır \;.


2
+1 bu kesinlikle bir dosya listesi oluşturmanız ve komutta kullanmanız gerektiğinde gitmenin yoludur. find -exec, yalnızca tek bir komut çalıştırabilmeyle sınırlıdır. Döngü ile kalbinizin içeriğine yönlendirebilirsiniz.
MaQleod

+1. Keşke daha fazla insan kullansaydı find. Yapılabilecek büyü var ve hatta algılanan sınırlamaları bile -execçözülebiliyor. -print0Seçeneği de kullanılmak için değerlidir xargs.
Ghoti

Döngü seçeneği gizli dizinleri göstermez. Bul seçeneği, sembolik bağlantıları göstermez.
jinawee

15

İçinde boşluk olan dosyalar için aşağıdaki gibi bir değişkeni alıntıladığınızdan emin olmalısınız:

 for i in $(ls); do echo "$i"; done; 

veya giriş alanı ayırıcı (IFS) ortam değişkenini değiştirebilirsiniz:

 IFS=$'\n';for file in $(ls); do echo $i; done

Son olarak, ne yaptığınıza bağlı olarak, ls'ye ihtiyacınız olmayabilir:

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

ilk örnekte bir alt kabuğun güzel kullanımı
Jeremy L,

IFS, ödevden sonra ve yeni karakterden önce neden bir $ 'a ihtiyaç duymaktadır?
Andy Ibanez

3
Dosya adları ayrıca yeni satırlar içerebilir. Ayrılmak \nyetersiz. Çıktısını ayrıştırmayı önermek hiçbir zaman iyi bir fikir değildir ls.
ghoti


4

Sadece CoverosGene'nin cevabını eklemek için, sadece dizin adlarını listelemenin bir yolu:

for f in */; do
  echo "Directory -> $f"
done

1

Neden IFS'yi yeni bir satıra ayarlamıyor, sonra lsbir dizideki çıktısını alıyorsunuz? IFS'yi yeni satıra ayarlamak, komik karakterlerle ilgili sorunları dosya adlarında çözmelidir; lsBir sıralama düzeni tesisi olduğundan, kullanmak güzel olabilir.

(Testte, IFS'yi ayarlamakta zorluk çekiyordum \nancak burada başka bir yerde önerildiği gibi newline backspace çalışmalarına ayarlamakta zorlandım ):

Örneğin (istenilen lsarama kalıbının geçtiğini varsayarsak $1):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

Bu özellikle OS X'te kullanışlıdır, örneğin, oluşturma tarihine göre (en eskiden en yeniye) sıralanan dosyaların listesini yakalamak için lskomuttur ls -t -U -r.


2
Dosya adları ayrıca yeni satırlar içerebilir ve kullanıcıların kendi dosyalarını adlandırmasına izin verildiğinde genellikle bunu yapar. Ayrılmak \nyetersiz. Yalnızca geçerli çözümler, yol adı genişletmeli bir for döngüsü kullanır veya find.
Ghoti

Dosya adlarının bir listesini aktarmanın tek güvenilir yolu, onları bir NUL karakteriyle ayırmaktır, çünkü bu kesinlikle bir dosya yolunda bulunmayan bir dosyadır.
glglgl

-3

Bunu böyle yapıyorum, ancak muhtemelen daha etkili yollar var.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt

6
Dosyanın yerine ls | while read i; do echo filename: $i; done
Jeremy L

Güzel. İki komut arasında $ EDITOR filelist.txt dosyasını da kullanabileceğinizi söylemeliyim. Bir düzenleyicide komut satırından daha kolay yapabileceğiniz birçok şey var. Bununla birlikte, bu soru ile ilgili değil.
AĞAÇ

Çözümünüz, yeni satırlar ve diğer süslü şeyler içeren dosya adlarıyla ilgili sorunu çözmez.
glglgl
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.