web-dev-qa-db-ja.com

テキストファイルソースからファイルの名前を変更するBashスクリプト

私はbashにかなり慣れていません。簡単なコマンドを1つずつ使用して、簡単な管理タスクを実行できます。ただし、名前の変更のソースとしてテキストファイルを使用して、ディレクトリ内のいくつかのファイルの名前を変更する必要があります。深みがないので、いくつかのポインタをいただければ幸いです。

説明させてください:

New File Name.xlsx 0.1  000011F4.dat 
New File Name.xlsx 0.2  000011F5.dat 
New File Name.xlsx 0.3  000011F6.dat 
New File Name.xlsx 0.4  000011F7.dat 
New File Name.xlsx 0.5  000011F8.dat 
New File Name.xlsx 0.6  000011F9.dat 

私が持っているソーステキストファイルは、上記にいくぶん似ています。最初の「列」はファイルの新しい名前、中央はバージョン、3番目は現在のファイル名です。

ディレクトリ内の.datファイルの名前を変更して、最初の列に表示されている名前に変更する必要があります。また、各ファイルの先頭にバージョン番号0.1、0.2などを追加する必要があります。

いくつかの質問を聞きたいんです:

ファイルに空白が含まれていることは大きな問題ですか?各ファイル文字列の周りに「」を追加したほうがよいでしょうか。

基本的にどこから始めればいいのかわからないので、助けていただければ幸いです。ご覧のとおり、通常の名前変更よりも少し複雑で、ファイル名の先頭とリストの空白にバージョン列を追加する必要があります。

6
user2472419

これはうまくいくはずです:

sh <(sed -r 's/^\s*(.*)\s+([0-9\.]+)\s+([0-9A-Z]{8}\.dat)\s*$/mv -iv \3 "\2 \1"/' files)

...ここで、filesはソースファイルの名前です。

これは、 プロセス置換 を使用して、sedコマンドの結果をsh(シェル)の新しいインスタンスに渡します。 sedコマンドの出力は次のとおりです。

mv -iv 000011F4.dat "0.1 New File Name.xlsx"
mv -iv 000011F5.dat "0.2 New File Name.xlsx"
mv -iv 000011F6.dat "0.3 New File Name.xlsx"
mv -iv 000011F7.dat "0.4 New File Name.xlsx"
mv -iv 000011F8.dat "0.5 New File Name.xlsx"
mv -iv 000011F9.dat "0.6 New File Name.xlsx"

sedコマンドを分解して、パターンを検索します。

  • ^-行の先頭
  • \s*-先頭の空白
  • (.*)-任意の文字(括弧は結果を\1に格納します)
  • \s+-少なくとも1つの空白文字
  • ([0-9\.]+)-0-9.の少なくとも1つ(\2に保存)
  • \s+-少なくとも1つの空白文字
  • ([0-9A-Z]{8}\.dat)-0-9またはA-Zの8文字、その後に.dat\3に格納)
  • \s*-末尾の空白
  • $-行の終わり

...そしてそれをmv -iv \3 "\2 \1"に置き換えます。ここで、\1から\3は以前に保存された値です。必要に応じて、バージョン番号とファイル名の残りの部分の間にスペース以外のものを使用できます。

結果は次のとおりです。

$ ls -l
total 60
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F4.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F5.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F6.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F7.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F8.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F9.dat
-rw-rw-r-- 1 z z 222 Aug  8 13:47 files
$ sh <(sed -r 's/^\s*(.*)\s+([0-9\.]+)\s+([0-9A-Z]{8}\.dat)\s*$/mv -iv \3 "\2 \1"/' files)
`000011F4.dat' -> `0.1 New File Name.xlsx'
`000011F5.dat' -> `0.2 New File Name.xlsx'
`000011F6.dat' -> `0.3 New File Name.xlsx'
`000011F7.dat' -> `0.4 New File Name.xlsx'
`000011F8.dat' -> `0.5 New File Name.xlsx'
`000011F9.dat' -> `0.6 New File Name.xlsx'
$ ls -l
total 60
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.1 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.2 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.3 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.4 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.5 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.6 New File Name.xlsx
-rw-rw-r-- 1 z z 222 Aug  8 13:47 files
7
user27282
sed 's/^\(.*\.xlsx\) \+\([[:digit:]]\+\.[[:digit:]]\+\) \+\(.[^ ]*\)/"\3" "\2\1"/' \
  <file_list | xargs -n 2 mv

これにより、行が.xlsxの前の部分に分割されます。これは、新しい名前の2番目の部分であり、\1としてアクセスできるようになります。バージョンを取得して\2に割り当てます。次に、末尾のスペースを無視して、古いファイル名が表示されます。

これは、引数としてmvに提供されたものとして引用されています。 -n 2は、mvが古いファイル名と新しいファイル名の2つの引数を受け取ることを保証します。

スペースは問題を引き起こしません。問題を複雑にするのは、入力リストが適切に構造化されていないことです。列が交換され、ファイル名が引用される場合は、事前の操作なしでxargsmvを使用できます。

4
Marco

ファイル名のスペース、およびいくつかの列の間に複数のスペースを使用すると、これが難しくなりますが、決して克服できないわけではありません。

リストファイルを1行ずつ読み取ります。通常は while IFS= read -r; do … を使用しますが、ここでは先頭と末尾の空白を削除する方が堅牢な場合があります。各行について:

  • 各行を3つの部分に分割します。これを行う1つの方法は、正規表現のマッチングを使用することです。 [[:space:]]+は1つ以上の空白文字(スペースまたはタブ)に一致します。 [[:space:]]+は、1つ以上の非空白文字に一致します。括弧で囲まれたグループは、BASH_REMATCH変数を介して取得できます。
    もう1つの方法は、ここではあまり便利ではありませんが、${VAR##PATTERN}${VAR%PATTERN}を使用して、変数からプレフィックスまたはサフィックスをそれぞれ削除することです。
  • 最後に移動を実行します。エラーをログに記録することを忘れないでください。

すべてを一緒に入れて:

ret=0
while read line; do
  if [[ $line =~ (.*[^[:space:]])[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+) ]]; then
    new_name="${BASH_REMATCH[1]}"
    version="${BASH_REMATCH[2]}"
    old_name="${BASH_REMATCH[3]}"
    mv -- "$old_name" "$version$new_name" || ret=1
  else
    echo "Malformed line: $line"
  fi
done <name_list.txt
exit $ret

awkの解決策は、次のコマンドを実行することです。

awk '{print "/bin/mv", $NF, "\"" $(NF-1), gensub(/^([^.]+\.xlsx).*/, "\\1", 1) "\"" | "bash" } ; END { close("bash") }' sourcefile

上記のコマンドは、コマンドの出力をbashシェルに渡します。

awk '{print "/bin/mv", $NF, "\"" $(NF-1), gensub(/^([^.]+\.xlsx).*/, "\\1", 1) "\""}' sourcefile

これを最初に実行して、本当に実行したいものであることを確認する必要があります。このawkコマンドは、ソースファイルの各行に対して、/bin/mvコマンド、行の最後の空白区切りフィールド、二重引用符、2番目のコマンドを出力します。行の最後のフィールドの後に、行全体を文字列.xlsxのすべてに置き換えた結果が続き、その後に二重引用符が続きます。

これがあなたが好むかもしれない変種です:

awk '{print "/bin/mv", $NF, "\"" "0." FNR, gensub(/^([^.]+\.xlsx).*/, "\\1", 1) "\"" | "bash" } ; END { close("bash") }' sourcefile

変数FNRは行番号です(したがって、ソースファイルからエントリ0.1、0.2、0.3、...を省略できます)。

ファイル名の空白は、私が「大規模な問題」と呼ぶものではありませんが、反対することをお勧めします。この最終バージョンのようなものを使用すると、新しいファイル名のスペースがアンダースコアに変更されます。

awk '{print "/bin/mv", $NF, "0." FNR "_" gensub(" ","_", "g", gensub(/^([^.]+\.xlsx).*/, "\\1", 1)) | "bash" } ; END { close("bash") }' sourcefile
0
Greg Marks