Dizindeki dosyalar üzerinde döngü ve yolu değiştirme ve dosya adına sonek ekleme


562

Programımı farklı argümanlarla başlatan bir senaryo yazmam gerekiyor, ama Bash için yeniyim. Programımı şununla başlıyorum:

./MyProgram.exe Data/data1.txt [Logs/data1_Log.txt].

İşte ne yapmak için pseudocode:

for each filename in /Data do
  for int i = 0, i = 3, i++
    ./MyProgram.exe Data/filename.txt Logs/filename_Log{i}.txt
  end for
end for

İlkinden ikinci argümanı nasıl oluşturacağımı gerçekten şaşırdım, bu yüzden dataABCD_Log1.txt gibi görünüyor ve programımı başlatıyor.

Yanıtlar:


736

İlk önce birkaç not: Data/data1.txtargüman olarak kullandığınızda , gerçekten olmalı /Data/data1.txt(önde gelen eğik çizgi ile)? Ayrıca, dış döngü yalnızca .txt dosyalarını mı yoksa / Data'daki tüm dosyaları mı taramalıdır? Aşağıda /Data/data1.txtyalnızca .txt dosyalarının olduğu varsayımıyla bir yanıt verilmiştir :

#!/bin/bash
for filename in /Data/*.txt; do
    for ((i=0; i<=3; i++)); do
        ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt"
    done
done

Notlar:

  • /Data/*.txt/ Data'daki ( / Data / bölüm dahil) metin dosyalarının yollarına genişler
  • $( ... ) bir kabuk komutu çalıştırır ve çıktısını komut satırındaki o noktaya ekler
  • basename somepath .txtsonuna kadar .txt kaldırılmış olarak somepatın temel kısmını çıktılar (ör. /Data/file.txt-> file)

MyProgram'ı Data/file.txtbunun yerine ile çalıştırmanız gerekiyorsa, baştaki eğik çizgiyi kaldırmak için /Data/file.txtkullanın "${filename#/}". Gerçekten eğer Öte yandan, Datadeğil /Datasen, sadece kullanımını taramak istediğiniz for filename in Data/*.txt.


1
Görünüşe göre basename -sstandart olmayan bir uzantı - Standart sözdizimini kullanmak için cevabımı düzenleyeceğim.
Gordon Davisson

21
Hiçbir dosya bulunamadı / joker ile eşleşen Eğer forops yürütme bloğu hala bir kez filename = "/Data/*.txt" ile girilir buluyorum. Bundan nasıl kaçınabilirim?
Oliver Pearmain

20
@OliverPearmain Döngüden shopt -s nullglobönce kullanın (ve daha shopt -u nullglobsonra sorunlardan kaçınmak için sonra kullanın) veya if [[ ! -e "$filename ]]; then continue; fidöngünün başına ekleyin , böylece var olmayan dosyaları atlar.
Gordon Davisson

9
Adlarında boşluk içeren dosyalar olduğunda bu çalışmaz.
Isa Hassen

4
@Isa Tüm çift tırnak işaretleri yerinde olduğu sürece boşlukla çalışmalıdır. Çift tırnaklardan herhangi birini dışarıda bırakın, boşlukla ilgili sorunlarınız olacaktır.
Gordon Davisson

345

İş parçacığının çoğaltılması için özür dileriz, ancak dosyaları globbing yoluyla yinelediğinizde, globun eşleşmediği köşe durumundan kaçınmak iyi bir uygulamadır (bu, döngü değişkeninin (eşleşmeyen) glob desen dizesinin kendisine genişlemesini sağlar).

Örneğin:

for filename in Data/*.txt; do
    [ -e "$filename" ] || continue
    # ... rest of the loop body
done

Referans: Bash Tuzaklar


8
Bu hala zamanında bir uyarı. Senaryomumu yanlış oluşturduğumu sanıyordum, ama benim dosya uzantısı büyük harf yerine küçük harf vardı, hiçbir dosya bulunamadı ve glob desen döndü. öf.
RufusVS

5
Bash etiketi kullanıldığından: bunun shopt nullglobiçin! (veya shopt failglobistediğiniz davranışa bağlı olarak da kullanılabilir).
gniourf_gniourf

Ayrıca, döngü sırasında onlara ulaşmadan önce, işlem sırasında silinen dosyaları da denetler.
loxaxs

1
Ayrıca dir.txt
vidstige

75
for file in Data/*.txt
do
    for ((i = 0; i < 3; i++))
    do
        name=${file##*/}
        base=${name%.txt}
        ./MyProgram.exe "$file" Logs/"${base}_Log$i.txt"
    done
done

Değiştirme name=${file##*/}( kabuk parametresi genişletme ), önde gelen yol adını sonuncuya kadar kaldırır /.

base=${name%.txt}İkame sondaki kaldırır .txt. Uzantılar değişebilirse biraz daha zor.


3
Kodunuzda bir hata olduğuna inanıyorum. Bunun base=${name%.txt}yerine bir satır olmalıdır base=${base%.txt}.
caseklim

4
@CaseyKlimkowsky: Evet; kod ve yorumlar aynı fikirde değilse, bunlardan en az biri yanlıştır. Bu durumda, bence sadece biri - kod; çoğu zaman, aslında her ikisi de yanlıştır. Bunu işaret ettiğiniz için teşekkürler; Ben düzelttim.
Jonathan Leffler

8

Dizin yapılarını güvenli bir şekilde yinelemek için read ile birlikte null ayrılmış çıktı seçeneğini kullanabilirsiniz.

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'\0' file; 
  do echo "$file" ;
done

Yani davan için

#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done

bunlara ek olarak

#!/bin/bash
while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done < <(find . -maxdepth 1 -type f  -print0)

while döngüsünü komut dosyasının (işlemin) geçerli kapsamında çalıştırır ve bulmanın çıktısının gerektiğinde değişkenlerin ayarlanmasında kullanılmasına izin verir


2
$'\0'tuhaf bir yazım şeklidir ''. Sen kaçırdığını IFS=ve -rgeçiş read: En okuma açıklamada olmalıdır: IFS= read -rd '' file.
gniourf_gniourf

Bazılarının arama $'\0'yapması ve bazı yığın noktalarının etrafına yayılması gerektiğini düşünüyorum . Belirttiğiniz düzenlemeleri yapacak. Olmamasından kötü etkileri nelerdir IFS=çalışırken echo -e "ok \nok\0" | while read -d '' line; do echo -e "$line"; doneherhangi olmayacak gibi görünüyor. Ayrıca -r Ben genellikle varsayılan, ancak olmasını önlemek için bir örnek bulamadık.
James

4
IFS=bir dosya adının boşlukla bitmesi durumunda gereklidir: try is with touch 'Prospero '(sondaki boşluğu not edin). Ayrıca, -rbir dosya adının ters eğik çizgi olması durumunda anahtara ihtiyacınız vardır: ile deneyin touch 'Prospero\n'.
gniourf_gniourf

-1

Görünüşe göre bir windows dosyası (.exe) yürütmeye çalışıyorsunuz. Powershell kullanmalısınız. Her neyse, bir Linux bash kabuğunda basit bir astar yeterlidir.

[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done

Veya bash

#!/bin/bash

for filename in /Data/*.txt; 
   do
     for i in {0..3}; 
       do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt; 
     done 
 done
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.