
次の 2 セットのファイルがあります:
file1.txt file1.json
file2.txt file2.json
file3.txt file3.json
...
fileN.txt fileN.json
JSON ファイルには次の形式が含まれます。
{ "ago": "59 sec ago", "base_time": 1401243133, "title": "Untitled", "type": "None", "retrieval_time": 1401624105, "id": "qwNAgvYZ" }
それぞれのファイルのパラメータfileX.txt
の値を使用してそれぞれの名前を変更したいと思います。title
fileX.json
例えば、
rename fileX.txt -> Untitled
その値は次のようにフィルタリングできます。
cat fileX.json | awk -F"\"" '{print $10}'
しかし、既存のファイル名に遭遇した場合は、サフィックスを付けてファイル名を変更する必要があります。
たとえば、既存の があるとしますUntitled.txt
。したがって、新しいファイルの名前を に変更する必要がありますUntitled-1.txt
。次に、 もUntitled-2.txt
同様に になります。
答え1
これは、非常にシンプルでわかりやすいシェルスクリプトです。jsonパイプ必要なことを実行します。高度な sh/bash 機能は使用せず、ファイル名の最低限の妥当性チェックのみを実行します。
注記:jqは よりもはるかに優れていますjsonpipe
が、 JSON データの構造を特に気にしない (または知りたい) 場合や、1 つまたは 2 つのフィールドのみを抽出したい場合、または、などjsonpipe
の行指向のテキスト処理ツールで JSON データを使用したい場合には、 の方がシンプルで使いやすくなりますawk
。sed
grep
printf
明らかに可能な機能強化の 1 つは、ゼロ埋めされた整数フィールドを使用して、ファイル名Untitled-0001.txt
を ではなく などの固定幅の番号付きの名前に変更することですUntitled-1.txt
。必要な場合は、これを実行してください。
書かれているとおり、実際にはファイル名は変更されません。mv
コマンドが出力されるだけです。だろう使用します。echo
各mv
コマンドの前の from を削除して、実際にファイル名を変更するように編集します。
#! /bin/sh
for f in file*.txt ; do
b=$(basename "$f" .txt)
# ignore current .txt file if there's no matching .json file
if [ -e "$b.json" ] ; then
# extract the title field.
title=$(jsonpipe < "$b.json" |
awk -F'\t' '$1=="/title" {gsub(/\"/,"",$2) ; print $2}')
if [ -n "$title" ] ; then
if [ ! -e "$title.txt" ] ; then
echo mv -v "$f" "$title.txt"
else
# are there any other "$title-*.txt" filenames?
others=$(find . -maxdepth 1 -name "$title-*.txt")
if [ -z "$others" ] ; then
echo mv -v "$f" "$title-1.txt"
else
# use version-sort to get highest $title- number used.
highest=$(printf "%s\n" "$others" | sort -V | tail -n 1)
hnum=$(printf "%s\n" "$highest" | sed -e 's/^.*-// ; s/\.txt$//')
hnum=$(( highest_num + 1))
echo mv -v "$f" "$title-$hnum.txt"
fi
fi
fi
fi
done
使用例 / 動作の証明:
$ ls -l
total 8
-rw-rw-r-- 1 cas cas 132 May 19 23:47 file1.json
-rw-rw-r-- 1 cas cas 0 May 20 00:04 file1.txt
-rwxrwxr-x 1 cas cas 797 May 20 00:04 json-rename.sh
$ cat file1.json
{"ago": "59 sec ago", "base_time": 1401243133, "title": "Untitled",
"type": "None", "retrieval_time": 1401624105, "id": "qwNAgvYZ"}
$ ./json-rename.sh
mv -v file1.txt Untitled.txt
$ touch Untitled.txt
$ ./json-rename.sh
mv -v file1.txt Untitled-1.txt
$ touch Untitled-1.txt
$ ./json-rename.sh
mv -v file1.txt Untitled-2.txt
$ touch Untitled-999.txt
$ ./json-rename.sh
mv -v file1.txt Untitled-1000.txt
答え2
for name in file*.txt; do
json=${name%.txt}.json
if [ -f "$json" ]; then
eval "$(
jq -r --arg name "$name" '[ "mv", "--", $name, .title ] | @sh' "$json"
)"
fi
done
これは、パターン に一致する現在のディレクトリ内のすべての名前をループしますfile*.txt
。このような名前ごとに、ファイル名のサフィックスを にjson
置き換えることで、変数に対応する JSON ファイル名が割り当てられます。.txt
.json
生成されたファイル名が既存のファイルに対応する場合、jq
その JSON ファイルを解析するために使用されます。現在のファイルの名前をドキュメント内のキーの文字列値に変更するためのシェル コマンドを構築します.title
。シェルは、ファイルの名前を変更する生成されたコマンドを評価します。
JSON ファイルの有効性はチェックされず、名前の競合は処理されません。
mv
名前の衝突を処理する最も簡単な方法は、GNU coreutils から使用していることを確認してから、そのオプションを使用することです--backup
。
jq
式を次のように修正します
jq -r --arg name "$name" '[ "mv", "-v", "--backup=numbered", "--", $name, .title ] | @sh' "$json"
GNU による番号付きバックアップとは、バックアップされたファイルの末尾に 、 などのファイル名サフィックスを追加することを意味します。また、GNUmv
に、実行内容に関する出力を取得するオプションを追加しました。.~1~
.~2~
-v
mv
mv
renamed 'file.txt' -> 'Untitled' (backup: 'Untitled.~2~')