
У меня есть два набора файлов:
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
Вот довольно простой и понятный скрипт оболочки, который используетjsonpipeделать то, что вы хотите. Он не использует никаких модных функций sh/bash и делает только минимальную проверку правильности имен файлов.
ПРИМЕЧАНИЕ:jqгораздо более функционален, чем jsonpipe
, но jsonpipe
проще и удобнее в использовании, когда вас не особенно интересует (или вы не хотите знать) структура данных JSON и вы хотите извлечь только одно или два поля и/или хотите использовать данные JSON с инструментами обработки текста, ориентированными на построчную обработку, такими как , awk
и т. д.sed
grep
Одним из очевидных возможных улучшений является использование printf
с целочисленным полем, дополненным нулями, для переименования файлов в пронумерованные имена фиксированной ширины, например, Untitled-0001.txt
вместо Untitled-1.txt
. Я оставлю это вам, если вы этого хотите.
Как написано, он на самом деле не переименует ни один файл. Он только выведет команду, mv
которую онбыиспользование. Отредактируйте его, удалив echo
from перед каждой mv
командой, чтобы он действительно переименовывал файлы.
#! /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 mv
означают добавление суффиксов имен файлов, таких как .~1~
, .~2~
и т. д., в конец резервных файлов. Я также добавил опцию -v
GNU mv
здесь, чтобы получить вывод о том, что mv
происходит, например
renamed 'file.txt' -> 'Untitled' (backup: 'Untitled.~2~')