Переименуйте файлы в обратном порядке

С помощью команды GNU sed R:

sed -e $'R fileb\nR fileb\nR fileb' filea > output
4
14.12.2019, 04:46
5 ответов

Сzsh:

autoload zmv # best in ~/.zshrc

typeset -A c=()
zmv -n '(*)_<->.txt(#qnOn)' '$1_$((++c[${(b)1}])).txt-renamed' &&
  : zmv '(*)-renamed' '$1'

(удалите-n(пробный прогон -)и :, если все устраивает (и не забудьте повторно -инициализировать c=()перед повторным запуском без пробного прогона )).

  • <->:похоже на <1-12>для соответствия десятичным числам в диапазоне, но здесь без указания границы, поэтому соответствует любой последовательности из одной или нескольких десятичных цифр. Также можно написать [0-9]##, где ##— эквивалент zshдля ERE +.
  • (#q...)— это явный синтаксис для указания квалификаторов glob .
  • n:сортирует по номерам
  • On:сортирует по имени в обратном порядке. Так что с nвыше, это сортирует список совпадающих файлов в обратном порядке.
  • Для замены,$1содержит то, что захвачено в (*), поэтому часть перед _<digits>.txt.
  • Добавляем $((++c[${(b)1}])), где $c— объявленный ранее ассоциативный массив.
  • ${(b)1}равно $1с экранированными символами глобуса (без него, это не будет работать должным образом, если $1содержит]).
  • мы делаем это в 2 этапа (добавляем суффикс -renamed, который удаляется на втором этапе ), чтобы избежать перезаписи файлов в процессе.

На вашем образце это дает:

mv -- data2_2.txt data2_1.txt-renamed
mv -- data2_1.txt data2_2.txt-renamed
mv -- data1_3.txt data1_1.txt-renamed
mv -- data1_2.txt data1_2.txt-renamed
mv -- data1_1.txt data1_3.txt-renamed

mv -- data1_1.txt-renamed data1_1.txt
mv -- data1_2.txt-renamed data1_2.txt
mv -- data1_3.txt-renamed data1_3.txt
mv -- data2_1.txt-renamed data2_1.txt
mv -- data2_2.txt-renamed data2_2.txt

Обратите внимание, что технически это не меняет порядок на обратный или делает это только в том случае, когда числа увеличиваются на единицу и начинаются с 1, как в вашем образце. Он превратит все [1, 2, 3], [4, 5, 6], [0, 10, 20]в [3, 2, 1].

Чтобы перевернуть список, это было бы немного сложнее. Это может быть что-то вроде:

all_files=(*_<->.txt(n))
prefixes=(${all_files%_*})

for prefix (${(u)prefixes}) {
  files=(${(M)all_files:#${prefix}_<->.txt})
  new_files=(${(Oa)^files}-renamed)
  for old new (${files:^new_files})
    echo mv -i -- $old $new-renamed
}

(удалить echoкогда счастлив ).

И снова запустите zmv '(*)-renamed' '$1'как вторую фазу.

В другом образце с дополнительным списком [0, 3, 10, 20]в качестве третьего примера, который дает:

mv -i -- data1_1.txt data1_3.txt-renamed
mv -i -- data1_2.txt data1_2.txt-renamed
mv -i -- data1_3.txt data1_1.txt-renamed
mv -i -- data2_1.txt data2_2.txt-renamed
mv -i -- data2_2.txt data2_1.txt-renamed
mv -i -- data3_0.txt data3_20.txt-renamed
mv -i -- data3_3.txt data3_10.txt-renamed
mv -i -- data3_10.txt data3_3.txt-renamed
mv -i -- data3_20.txt data3_0.txt-renamed

Эти решения не предполагают, какой символ (или не -символ )могут содержать имена файлов, не будут переименовывать файлы, если они не заканчиваются на _<digits>.txt. Подход, основанный на zmv-, защитит от перезаписи файлов, названных с суффиксом -renamed, который был бы там заранее, а не последний подход (, хотя -iзаставит mvподсказать вам, прежде чем это произойдет ). Кроме того, вместо добавления суффикса -renamedвы можете переместить переименованный файл в каталог renamed.

2
27.01.2020, 20:59

Вот простое решение, и я думаю, что оно отлично подойдет для вашей ситуации. Здесь мы сначала проверяем, сколько типов файлов мы получили по префиксу. Здесь под префиксом типа файла я имел в виду data1 _, data2_и так далее. Затем для каждого типа префикса мы получаем общее количество доступных файлов и сохраняем их в массиве с именем totalFilesForEachPrefix.

Затем на шаге 1 мы переименовываем файл во временный. Причина перехода на расширение otemp— избежать конфликта имен и перезаписать существующий файл. Здесь я предположил, что у вас есть файлы типа 1 _1 1 _2 1 _3 1 _4 и так далее.

Затем, на шаге 2, мы просто избавляемся от .otempрасширения.

#!/usr/bin/env bash
totalFileTypeByPrefix=$( for file in data*_1.txt; do echo $file; done | wc -l )
totalFilesForEachPrefix=()
for (( prefix = 1; prefix <= totalFileTypeByPrefix; prefix++ )); do
  totalFilesForEachPrefix+=( $( for file in data${prefix}_*.txt; do echo $file; done | wc -l) )
done
### Step 1
prefix=1; type=0;
while (( prefix <= totalFileTypeByPrefix )); do
  suffix=${totalFilesForEachPrefix[$type]}
  for file in data${prefix}_*.txt; do
    mv $file data${prefix}_${suffix}.txt.otemp; echo "$file renamed temporary --> data${prefix}_${suffix}.txt.otemp"
    suffix=$((suffix -1))
  done
  type=$((type+1))
  prefix=$((prefix+1))
done
### Step 2
echo "....Finally changing the temporary files"....
for tempfile in *.otemp; do
  file=${tempfile::-6};
  mv $tempfile $file; echo "$tempfile renamed final --> $file";
done

Вот вывод с объяснением того, что происходит:

data1_1.txt renamed temporary --> data1_3.txt.otemp
data1_2.txt renamed temporary --> data1_2.txt.otemp
data1_3.txt renamed temporary --> data1_1.txt.otemp
data2_1.txt renamed temporary --> data2_2.txt.otemp
data2_2.txt renamed temporary --> data2_1.txt.otemp
....Finally changing the temporary files....
data1_1.txt.otemp renamed final --> data1_1.txt
data1_2.txt.otemp renamed final --> data1_2.txt
data1_3.txt.otemp renamed final --> data1_3.txt
data2_1.txt.otemp renamed final --> data2_1.txt
data2_2.txt.otemp renamed final --> data2_2.txt
0
27.01.2020, 20:59

Глупый, но всегда работающий метод (следующий Принципу KISS)заключается в следующем:

/bin/ls data*.txt >rename.sh
# edit the file so that instead of
# data1_1.txt
# each line reads
# mv data1_1.txt data1_3.txt
# (you can use column selection in your editor)
sh rename.sh

Конечно, это очевидно, но обычно это намного быстрее, чем возиться со сложными (и одним -из всех )скриптов.

(отредактировано позже )Комментарии верны, что в этом случае использование этой техники может привести к потере данных(data1_3.txtможет быть уничтожено ). Поэтому безопасно сделать перемещение в два шага :сначала изменить имена, но переместить файл в какое-то безопасное место (либо добавить произвольное ложное расширение, либо создать временный подкаталог, куда перемещаются все файлы ), и затем на втором этапе переместите все файлы в их конечное место назначения.

-1
27.01.2020, 20:59

Вот фрагмент bash для выполнения этой работы, предполагая, что файлы действительно названы так, как вы их изобразили(data<one-digit>_<digits>.txt).

shopt -s extglob

#gather files into array
files=( data[[:digit:]]_+([[:digit:]]).txt )

#zip original files with their target file names and feed to mv
paste <(printf '%s\n' "${files[@]}" | sort -k1.5,1n -k2n -t'_') \
    <(printf '%s.ren\n' "${files[@]}" | sort -k1.5,1n -k2nr -t'_') | 
    xargs -n 2 mv --

#strip the temporary.ren suffix
for f in data*.ren; do mv -- "$f" "${f%.ren}"; done
1
27.01.2020, 20:59

Сначала переименуйте все файлы с префиксом «старый -»:

for i in *
do
    mv "$i" "old-$i"
done

Затем запустите эту команду и просмотрите вывод, чтобы убедиться, что он выглядит хорошо:

ls -v | tac | sort -s -t _ -k1,1 | sed -e 's/^old-//' | paste <(ls -v) - | sed -e 's/^/mv /'

Если это так, направьте вывод в sh.

Вот что происходит.

  • ls -vпроизводит их в отсортированном порядке (-v говорит о сортировке 11 после 9, например)
  • tac переворачивает весь ввод (весь файл; потерпите меня!)
  • Сортировка говорит о стабильной сортировке только символов до первого _. Оба параметра-k1,1и-sважны для получения правильного результата. Без -k1,1 остальная часть строки используется для разрешения дубликатов, которые нам не нужны, а без -sдубликаты упорядочиваются произвольно.

Остальное достаточно просто.

1
27.01.2020, 20:59

Теги

Похожие вопросы