web-dev-qa-db-ja.com

Bashを使ってディレクトリ内のファイルを反復処理する方法は?

私は自分のプログラムを異なる引数で起動するスクリプトを書く必要がありますが、私はBashが初めてです。私はこのようにプログラムを始めます。

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

これが私がやりたいことの擬似コードです。

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

だから私は本当に最初のものから2番目の引数を作成する方法に困惑しているので、それはdataABCD_Log1.txtのように見えて私のプログラムを開始します。手助けは大歓迎です。

P.S似たような質問があることは知っていますが、ログファイル名の作成には何も見つかりませんでした。

446
Dobrobobr

最初にいくつか注意してください。引数としてData/data1.txtを使用するとき、それは本当に/Data/data1.txtであるべきですか?また、外側のループは.txtファイルのみをスキャンするのか、それとも/ Data内のすべてのファイルをスキャンするのですか? /Data/data1.txtと.txtファイルのみを仮定して、これが答えです。

#!/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

ノート:

  • /Data/*.txtは/ Data内のテキストファイルのパスに展開されます(含む the/Data /部分)
  • $( ... )はシェルコマンドを実行し、その出力をコマンドラインのその位置に挿入します。
  • basename somepath .txtはsomepathのベース部分を、末尾から.txtを削除して出力します(例:/Data/file.txt - > file

MyProgramをData/file.txtではなく/Data/file.txtで実行する必要がある場合は、"${filename#/}"を使用して先頭のスラッシュを削除します。一方、スキャンしたいのが実際には/DataではなくDataである場合は、for filename in Data/*.txtを使用するだけです。

576
Gordon Davisson

スレッドをネクロミスして申し訳ありませんが、globを使用してファイルを反復処理するときは、globが一致しない(ループ変数が(一致しない)globパターン文字列自体に展開される)コーナーケースを避けることをお勧めします。

例えば:

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

参照: バッシュの落とし穴

235
Cong Ma
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

name=${file##*/}置換( シェルパラメータ展開 )は、最後の/までの先頭のパス名を削除します。

base=${name%.txt}置換は末尾の.txtを削除します。拡張子が変わる可能性がある場合は、少し面倒です。

66

安全にディレクトリ構造を反復処理するために、readを使用してfinds nullで区切られた出力オプションを使用できます。

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

だからあなたの場合

#!/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

さらに

#!/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ループを実行し、必要に応じて変数の設定にfindの出力を使用できるようにします。

3
Arcabard