web-dev-qa-db-ja.com

同じ名前のファイルを同じディレクトリにコピーする

genome.txtという名前のファイルを含むフォルダーが多数あり、それらを同じフォルダーにcpする必要があります。ファイルがgenome1.txtgenome2.txtなどのように見えるようにする方法を見つけようとしています。これに対する簡単な解決策を探しています。

4
JS Brewer

シェルスクリプトとその使用法

これは、find ...|while read var ; do ... done構造体(ファイル名の処理で非常に一般的)を使用する単純なシェルスクリプトで実行できます。以下のbashスクリプトは、現在の(最上位の)ディレクトリで動作し、宛先に単一の引数を取ります。

#!/bin/bash
find -name "genome.txt" -print0 | while IFS= read -r -d '' path
do
    base=$(basename --suffix=".txt"  "$path")
    new_path="${1%/}"/"$base"$counter".txt"
    echo "$path" "$new_path"
    counter=$(( $counter +1 ))
done

>>> NOTE <<<:スクリプトは、テスト目的でechoを使用します。結果のパスに満足したら、echomvで置き換えてすべてのファイル名を移動するか、cpですべてのファイル名をコピーします。

例:

bash-4.3$ tree
.
├── destination
├── dir1
│   └── genome.txt
├── dir2
│   └── genome.txt
├── dir3
│   └── genome.txt
└── move_enumerated.sh

4 directories, 4 files
bash-4.3$ ./move_enumerated.sh  ./destination
./dir2/genome.txt ./destination/genome.txt
./dir3/genome.txt ./destination/genome1.txt
./dir1/genome.txt ./destination/genome2.txt

スクリプトを改善して柔軟性を高める

このスクリプトをさらに改善して、より一般的にすることができます。ユーザーは、コマンドライン引数として、ファイル名、通過する最上位ディレクトリ、および宛先をすべて指定できます。

#!/bin/bash
find "$2"  -name "$1" -print0 | while IFS= read -r -d '' path
do
    base=$(basename --suffix=".txt"  "$path")
    new_path="${3%/}"/"$base"$counter".txt"
    echo "$path" "$new_path"
    counter=$(( $counter +1 ))
done

テスト走行:

bash-4.3$ ./move_enumerated.sh "genome.txt"  "./testdir" "./testdir/destination"
./testdir/dir2/genome.txt ./testdir/destination/genome.txt
./testdir/dir3/genome.txt ./testdir/destination/genome1.txt
./testdir/dir1/genome.txt ./testdir/destination/genome2.txt

構文と動作理論

すべてのスクリプトでcommand | while read variable ; do ... done構造体を使用しています。これは非常に一般的なアプローチであり、lsおよびスクリプトを壊す可能性のある難しいファイル名の処理を避けるために頻繁に使用されます。

パイプの左側にfindコマンドがあります。このコマンドは、ディレクトリを引数として取ります(指定しない場合、Linuxで使用されるGNU findは、.-現在の作業ディレクトリ)。他のオプションには、検索する特定のファイル名である-nameと、印刷不能な-print0文字で区切られた結果を出力するために使用される\0が含まれます。改行またはその他の文字での分割を避けるために頻繁に使用されます。これらの文字はファイル名自体の中に現れる可能性があり、その結果、スクリプトが壊れる可能性があるためです。

パイプの右側にwhile IFS= read -r -d '' ; do . . . done構造があります。 whileシェルが組み込まれたreadループは、実際のstdin入力に頻繁に使用されます。この場合、これはパイプから取得されます。 IFS=-rおよび-d ''は、ファイル名を安全に受け取り、各アイテムが\0で区切られていることを確認するために必要です。

スクリプトの残りの部分はかなり簡単です。 basenameコマンドを使用して、ファイルのベース名を抽出します。この場合、既知の拡張子を具体的に扱っており、ファイル名に単一のドットが含まれることを想定しているため、--suffix=".txt"を使用してその部分を取り除き、genome部分を残します。次に、destination、basename、およびcounter変数を結合して、ファイルへの新しいパスを作成します。改良されたスクリプトでは、"${3%/}"引数(宛先フォルダー)でパラメーター拡張を使用していることに注意してください。これは、ユーザーがコマンドラインで/文字(./destinationまたは./destination/)を追加したかどうかに関係なく、裸のディレクトリ名のみを抽出し、ベース名と異なる/を介して結合することを保証するために行われます。また、counter変数は最初は設定されていないため、最初に受け取るファイル名はgenome.txtになります。その後、カウンター変数がインクリメントされて作成され、他のファイル名を処理するときに表示されます。

詳細については、 シェルのファイル名とパス名:正しく実行する方法 を参照してください。

4