ディレクトリ内のファイルをループしてパスを変更し、ファイル名にサフィックスを追加する方法


562

さまざまな引数でプログラムを開始するスクリプトを記述する必要がありますが、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 for
end for

最初の引数から2番目の引数を作成する方法に本当に困惑しているので、dataABCD_Log1.txtのように見え、プログラムを開始します。

回答:


736

最初にいくつかの注意事項: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内のテキストファイルのパスに展開します(/ Data /部分を含む
  • $( ... ) シェルコマンドを実行し、その出力をコマンドラインのその時点で挿入します
  • basename somepath .txtsomepathの基本部分を出力し、末尾から.txtを削除します(例/Data/file.txt-> file

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


1
どうやらbasename -s非標準の拡張機能です-標準の構文を使用するように回答を編集します。
Gordon Davisson

21
ファイルが見つからない場合やワイルドカードに一致する場合、for loops実行ブロックがfilename = "/Data/*.txt"で1回入力されていることがわかります。どうすればこれを回避できますか?
Oliver Pearmain 2015

20
@OliverPearmain shopt -s nullglobループの前に(shopt -u nullglob後で問題を回避するために後で)使用するか、ループif [[ ! -e "$filename ]]; then continue; fiの先頭に追加して、存在しないファイルをスキップします。
Gordon Davisson、2015

9
名前に空白を含むファイルがある場合、これは機能しません。
Isa Hassen 2016年

4
@Isa二重引用符がすべて配置されている限り、空白で機能します。二重引用符は省略してください。空白を使用すると問題が発生します。
Gordon Davisson 2016年

345

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

例えば:

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

リファレンス:Bashの落とし穴


8
これはまだタイムリーな警告です。スクリプトを誤って作成したと思いましたが、ファイル拡張子が大文字ではなく小文字になり、ファイルが見つからず、globパターンが返されました。うん。
RufusVS 2017

5
Bashタグが使用されているため、それshopt nullglobが目的です。(またはshopt failglob、必要な動作に応じて使用することもできます)。
gniourf_gniourf 2017

さらに、これはループがそれらに到達する前に、処理中に削除されたファイルをチェックします。
loxaxs 2018年

1
また、あなたが持っているというディレクトリケースハンドル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

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

base=${name%.txt}置換は末尾を削除します.txt。拡張子が異なる場合は少し注意が必要です。


3
コードに誤りがあると思います。1行はのbase=${name%.txt}代わりににする必要がありbase=${base%.txt}ます。
caseklim 2014年

4
@CaseyKlimkowsky:はい。コードとコメントが一致しない場合、それらの少なくとも1つが間違っています。この場合は、コードは1つだけだと思います。多くの場合、実際には両方が間違っています。ご指摘いただきありがとうございます。私はそれを修正しました。
ジョナサンレフラー、2014年

8

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の出力を変数の設定に使用できるようにします


2
$'\0'奇妙な書き方''です。欠落IFS=していて、次の-rように切り替えreadます:読み取りステートメントは次のようになりますIFS= read -rd '' file
gniourf_gniourf

$'\0'いくつかのスタックポイントを検索して広げる必要があると思います。あなたが指摘した編集を行うつもりです。IFS=試していないことの悪影響は何もないecho -e "ok \nok\0" | while read -d '' line; do echo -e "$line"; doneようです。また、多くの場合-rがデフォルトですが、これが発生を防ぐ例を見つけることができませんでした。
James

4
IFS=ファイル名がスペースで終わる場合に必要です:try is with touch 'Prospero '(末尾のスペースに注意してください)。また-r、ファイル名にバックスラッシュがある場合は、スイッチが必要touch 'Prospero\n'です。
gniourf_gniourf

-1

Windowsファイル(.exe)を実行しようとしているようです。きっとpowershellを使用しているはずです。とにかく、Linux bashシェルでは、単純なワンライナーで十分です。

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

またはバッシュで

#!/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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.