genome.txt
という名前のファイルを含むフォルダーが多数あり、それらを同じフォルダーにcp
する必要があります。ファイルがgenome1.txt
、genome2.txt
などのように見えるようにする方法を見つけようとしています。これに対する簡単な解決策を探しています。
これは、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
を使用します。結果のパスに満足したら、echo
をmv
で置き換えてすべてのファイル名を移動するか、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
になります。その後、カウンター変数がインクリメントされて作成され、他のファイル名を処理するときに表示されます。
詳細については、 シェルのファイル名とパス名:正しく実行する方法 を参照してください。