Переименовать все файлы в каталоге с помощью bash, уменьшив имя файла на 1

#bash #shell

#bash #оболочка

Вопрос:

Есть ли способ переименовать каталог, полный файлов, названных в соответствии со следующим соглашением, без использования регулярных выражений? Например, у меня есть файлы с именами 1.json , 2.json , 3.json , 4.json и я хочу, чтобы все они были переименованы в их текущие #{NUM} #{NUM - 1} , чтобы результирующий набор был: 0.json , 1.json , 2.json , 3.json .

Используя соглашение:

 for i in *.json; do mv -- "$i" ..
 

Возможно ли это сделать без регулярных выражений? Или я застрял при анализе имени файла, преобразовании строки в целое число и переходе оттуда?

Комментарии:

1. вы показываете только имена файлов с однозначными цифрами, не могли бы вы также иметь имена файлов с 2-, 3-, более-? цифры?

2. @markp-fuso да, и я только что увидел ваш комментарий ниже, чтобы понять, почему это актуально. Я просто опустил их для краткости, но вижу проблему в этом.

Ответ №1:

${var%suffix} удалит завершающий суффикс. Вы можете использовать его для удаления .json и получения только чисел.

 for file in *.json; do
    i="${file%.json}"
    echo mv -- "$i".json "$((i-1))".json
done
 

Удалите echo , если это выглядит хорошо.


Обратите внимание, что это будет обрабатывать только однозначные имена файлов. Для обработки многозначных имен файлов нам нужно, чтобы они были отсортированы естественным образом, а не лексикографически. Лексикографическая сортировка ставит 10 перед 9 , поскольку символ 1 меньше 9 , чем , что означает 10.json , что он будет переименован 9.json до 9.json переименования 8.json . Естественная сортировка, напротив, будет сортироваться 9 раньше 10 .

К сожалению, это делает все это в десять раз более загадочным. Глобусы не могут быть отсортированы в строке, поэтому нам приходится перемещать вещи, чтобы мы могли передавать список имен файлов sort -g . Затем, чтобы перебрать поток имен файлов, мы должны перейти от for цикла к while read циклу с заменой процесса.

Базовая структура скелета выглядит следующим образом:

 while read file; do
    ...
done < <(ls *.json | sort -g)
 

Затем мы добавим несколько улучшений:

  1. Используйте while IFS= read -r , чтобы сделать синтаксический анализ более надежным.
  2. Избегайте синтаксического ls анализа, переключившись на printf метод на основе a для печати имен файлов.
  3. Используйте разделители NUL вместо новых строк, поскольку новые строки являются технически допустимыми символами в именах файлов. Это означает добавление to printf , -z to sort и -d $'' to read , чтобы каждый из трех выдавал и потреблял NUL.

Эти шаги являются хорошими общими привычками, но я признаю, что они не очень важны для данного конкретного скрипта, и они делают код похожим на монстра Франкенштейна. Я показываю их здесь в педагогических целях. Я не скажу, удалите ли вы их из своего одноразового скрипта личного использования.

Окончательное, надежное решение:

 while IFS= read -rd $'' file; do
    i="${file%.json}"
    echo mv -- "$i".json "$((i-1))".json
done < <(printf '%s' *.json | sort -zg)
 

Комментарии:

1. все еще есть возможность перезаписи, если существуют такие имена файлов, как «011,json» и «11.json». Кроме того, вы получите ошибки, если попытаетесь сделать $((i-1)) , когда i `082″ или что-то в этом роде. 😉

2. Это обработает недопустимые восьмеричные числа и выведет новое число с тем же заполнением нуля, что и исходное: wid=${#i}; new=$(printf "%0*d.json" "$wid" $((10#$i - 1)) ); mv -- "$f" "$new" — хотя оно переименует «1000.json» в «0999.json» ( if потребуется дополнительное)

Ответ №2:

Вы можете использовать это awk основанное на этом решение:

 printf '%sn' [1-9]*.json | sort -n | 
awk -F. '{print "mv", $0, ($1-1 FS $2)}' | bash

mv 1.json 0.json
mv 2.json 1.json
mv 3.json 2.json
mv 4.json 3.json
mv 5.json 4.json
 

Как только вы будете довольны результатом, вы можете удалить echo его раньше mv .

Ответ №3:

попробуйте этот код. simpy на -1 от имени файла. но если ваше имя файла содержит ноль в первом имени, допустим 001.json ..n тогда есть 1.json , этот код не будет работать. потому что это заменит текущий 1.json файл

 declare -i newName=0
for i in `ls | sort -n | grep ".json"`
do
  newName=${i%%.*}-1
  mv -- "$i" "$newName.json"
done
 

Комментарии:

1. в for я думаю, вы можете выполнить сортировку по изменению этого для i в ls | sort -n | grep ".json"

2. это не удается для имени файла, подобного 082.json . также перезапись все еще возможна, если имена файлов, такие как «011.json» и «11.json», существуют вместе.

3. Пожалуйста, обратите внимание: почему бы не выполнить синтаксический ls анализ?