it-swarm.asia

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

Herhangi biri, dizin adlarının bir listesi için ls ile bir şeyler yapmak ve her birini dolaşmak ve bir şeyler yapmak için bir şablon Shell betiğine sahip mi?

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

87
Daniel A. White

@ shawn-j-goff ve diğerlerinin önerdiği gibi, bir kürenin nerede kullanacağını kullanmamak için düzenlendi.

Sadece bir for..do..done döngüsü kullanın:

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

**.txt veya bir liste (örneğin, dosyalar, dizinler veya bu konuda herhangi bir şey) döndüren başka bir glob ile, örneğin bir liste oluşturan bir komut, örneğin, $(cat filelist.txt) veya aslında bir listeyle değiştirebilirsiniz.

do döngüsünün içinde sadece dolar işareti öneki olan döngü değişkenine başvurursunuz (yukarıdaki örnekte $f). echo dosyasını kullanabilir veya istediğiniz başka bir şey yapabilirsiniz.

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

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
100
CoverosGene

Dosya adlarını almak için lsçıktısının kullanılması kötü bir fikirdir . Arıza ve hatta tehlikeli senaryolara yol açabilir. Bunun nedeni, bir dosya adının / ve nullname__character dışındaki karakterleri içermesi ve lsöğesinin sınırlayıcı olarak bu karakterleri kullanmamasıdır; bu nedenle, bir dosya adında boşluk veya yeni satır varsa, siz will beklenmeyen sonuçlar elde edersiniz.

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

Bunlardan ilki, Shell'in yerel globbing özelliklerini kullanmaktır.

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

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

Eğer tekrar tekrar alt dizinlere gitmek istiyorsanız, Shell'inizde genişletilmiş bir globbing özelliği bulunmadıkça (örneğin bashname __ 'in globstargibi) bu olmaz. (Shell'in bu özelliklere sahip olmaması durumunda ya da betiğinizin çalışmasını sağlamak istiyorsanız çeşitli sistemlerde, sonraki seçenek findişlevini kullanmaktır.

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

Burada findkomutu echoöğesini çağıracak ve 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.

-exec argümanının sözdizimi biraz komik görünüyor. findname__, -exec 'dan sonra ilk argümanı alır ve programın çalıştırılması ve sonraki her argümanın o programa geçmesi için bir argüman olarak kabul eder. -exec'nun görmesi gereken iki özel argüman var. İlki {}; bu argüman, findöğesinin önceki bölümlerinin oluşturduğu bir dosya adıyla değiştirilir. İkincisi, ;, findöğesinin programa iletilecek argümanlar listesinin sonu olduğunu bilmesini sağlar; findbunu gerektirir çünkü findiçin amaçlanan ve yürütülen program için tasarlanmamış daha fazla argümanla devam edebilirsiniz. \'nin nedeni, Shell'in ayrıca ;'yi de özel olarak ele almasıdır - bir komutun sonunu temsil eder, bu yüzden kaçmamız gerekir, böylece Shell'in kendisi için tüketmek yerine onu findname__'ye bir argüman olarak verir; Shell'in özel olarak işlem görmemesini sağlamanın bir başka yolu onu tırnak işaretleri içine almaktır: ';' bu amaçla sadece \; işlevini yerine getirir.

64
Shawn J. Goff

İç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;
14
john

Eğer GNU Paralel http://www.gnu.org/software/parallel/ yüklü ise bunu yapabilirsiniz:

ls | parallel echo {} is in this dir

.Txt dosyasının tümünü .xml olarak yeniden adlandırmak için:

ls *.txt | parallel mv {} {.}.xml

GNU için giriş videosunu izleyin _ Daha fazla bilgi için paralel olun: http://www.youtube.com/watch?v=OpaiGYxkSuQ

5
Ole Tange

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

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

Neden IFS'yi yeni bir satıra ayarlamıyor, sonra bir dizideki ls çıktısını alıyorsunuz? IFS'yi yeni satıra ayarlamak, komik karakterlerle ilgili sorunları dosya adlarında çözmelidir; ls işlevini kullanmak, yerleşik bir sıralama özelliğine sahip olduğundan Nice olabilir.

(Testte, IFS'yi \n olarak ayarlamada sorun yaşıyordum ancak burada başka yerde önerildiği gibi newline backspace işlemlerine ayarlamakta zorlandım):

Örneğin. (İstenilen ls arama modelinin $1 içinde geçtiğini varsayarak):

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, örneğin oluşturma tarihine göre (en eskiden en yeniye) sıralanan dosyaların listesini yakalamak için kullanışlıdır, ls komutu ls -t -U -r şeklindedir.

1
jetset