Файлы «перезаписаны», пространство все еще занято, они потеряны?

11

Настолько глупо, что я использовал следующий сценарий на моем сервере 19.04 в попытке переместить несколько видеофайлов в папки с префиксами:

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
shopt -s nocasematch

for file in *
do
    for dir in "${dirs[@]}"
    do

     if [ -d "$file" ]; then
      echo 'this is a dir, skipping'
      break
     else
      if [[ $file =~ ^[$dir] ]]; then
       echo "----> $file moves into -> $dir <----"
       mv "$file" "$dir"
       break
      fi
     fi
  done
done

Понятия не имею, где это пошло не так, но вместо того, чтобы перемещать файлы в папки, они пошли в единый вывод ..

----> a1.ts moves into -> A <----
----> a2.ts moves into -> A <----
----> a3.ts moves into -> A <----
----> a4.ts moves into -> A <----
----> a5.ts moves into -> A <----
----> c1.ts moves into -> C <----
----> c2.ts moves into -> C <----
----> c3.ts moves into -> C <----
----> c4.ts moves into -> C <----
----> c5.ts moves into -> C <----

К счастью, я остановил процесс (CTRL + C), как только заметил, что он идет не так, как задумано, и не прошел всю папку.

Так что теперь у меня есть эти файлы Aи C, которые меньше, чем Гб, и, судя по всему, это одно видео.

В общем объеме использования самой папки на диске не хватает 50 ГБ, но общее дисковое пространство компьютера осталось прежним. Заставить меня думать, что файлы не удалены?

Любая помощь приветствуется, спасибо :)

Редактировать: файлы фактически исчезли, остается только последний записываемый файл, все, что потребовалось, - это некоторое время, чтобы обновить информацию об использовании диска. Мораль истории, запускайте свои сценарии на фиктивных файлах раньше!

Я калькулятор TI
источник
3
И сделали каталоги по имени A, Bи так далее существовали перед запуском сценария? Если нет, вы просто переименовали файлы. Все файлы, имена которых начинаются с aили Aбыли переименованы A, поэтому сохранился только последний переименованный файл, остальные перезаписываются. Для вызова переменной dirне создается каталог!
mook765
2
так он это и интерпретировал. "последний переименованный файл выжил" ха. Каталоги не существует, я должен был добавить «прикосновение» для каждого перед рукой. спасибо за разъяснение
я калькулятор TI
4
+1 за ".. мораль этой истории, запускайте свои сценарии на фиктивных файлах раньше!"
Судод
4
Совет, чтобы избежать таких проблем: Используйте mv "$file" "$dir/", с трейлингом /; тогда, если $dirне существует, mvпроизойдет ошибка вместо переименования $fileв $dir. Также рассмотрим mv -iи mv -n. И всегда делайте mkdir -pперед переездом, для хорошей меры.
Марсель
3
@sudodus Еще лучше: «Всегда делайте резервные копии своих данных!».
Джон Бентли

Ответы:

15

Я думаю, что это проблема: вы должны были создать каталоги A, B, C ... Z. Если вы это сделали, mvкоманда должна была переместить файлы в эти каталоги.

Но если нет, mvкоманда перемещает файлы в файлы с этими именами, A, B, C ... и я думаю, что это то, что вы сделали.

Чтобы сделать шеллскрипт более безопасным, вы должны создать каталоги (если их там еще нет), прежде чем начать перемещение.

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

for dir in "${dirs[@]}"
do
 mkdir -p $dir
done

Если вы хотите, чтобы все mvстало еще безопаснее, вы также можете использовать -iопцию

   -i, --interactive
          prompt before overwrite
sudodus
источник
1
Будет ли добавление touchхорошим подспорьем, чтобы mkdirизбежать конфликтов в случае многократного запуска скрипта?
Я калькулятор TI
2
touchсоздает файл, если имя не существует. Так что он не будет делать то, что вы хотите в этом случае. mkdir -pможет справиться с использованием сценария несколько раз.
Судодос
6
Еще один простой способ , вы можете сделать mvболее безопасным, чтобы получить в привычку добавлять слэш имя цели , когда цель является каталогом т.е.mv "$file" "$dir/"
steeldriver
7

@Sudodus уже объяснил, что пошло не так, но вот более простая версия вашего сценария в следующий раз:

for letter in {a..z}; do 
    dir=${letter^}
    mkdir -p -- "$dir" 
    mv -- "$letter"* "${letter^^}"* "$dir"/
done

объяснение

  • for letter in {a..z}; do: {a..z}расширяется на все строчные буквы между aи z:

    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z

    Так что это будет перебирать все строчные буквы, сохраняя каждую как $letter.

  • dir=${letter^}: синтаксис ${var^^}возвращает содержимое переменной $varс первым символом в верхнем регистре (так как он имеет только один символ, это все, что нам нужно). Таким образом, если $letterесть a, то ${letter^^}есть A, и, следовательно, $dirбудет верхний регистр версии текущего $letter.

  • mkdir -p -- "$dir": создать каталог. Если он уже существует, ничего не делать ( -p). Знак -- обозначает конец параметров и полезен для защиты от имен, начинающихся с -.
  • mv -- "$letter"* "${letter^}"* "$dir" : переместить каждый файл (или каталог) в соответствующую цель.

Проблема в том, что он также будет перемещать любые каталоги, которые у вас могут быть. Он не будет перемещать целевые каталоги, потому что либо они еще не существуют, либо вы попытаетесь переместить их в себя, но любые существующие каталоги, которые не являются целевыми, будут перемещены.

Если это проблема, вам придется сделать что-то вроде этого:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
terdon
источник
В чем разница между ${letter^}и ${letter^^}, и если они идентичны, зачем использовать ${letter^^}вместо $dir?
Фонд Моника иск
1
@NicHartley ${var^}использует только ${var^^}заглавные буквы , а все буквы - заглавные. Это не имеет никакого значения здесь, так как $letterимеет только одну букву.
Тердон
Это идеальный ответ, за исключением того, что вы можете добавить дополнительный уровень осторожности, добавив $dirв mvкоманду косую черту каталога . (В его нынешнем виде он потерпит неудачу, если файл уже существует с заглавным именем из одной буквы)
Стиг Хеммер,
@ Стигхеммер, упс, да, действительно. Очень хорошая мысль, спасибо. Ответ отредактирован.
Тердон
4

Вместо проверки каждого файла по массиву словаря, который создает много итераций, вы можете сопоставлять файлы с шаблонами.

Очень простой вид:

#!/bin/bash

videos=./videos
sorted=./sorted

# sort types link,move.
sort_type=link

find "$videos" -maxdepth 1 -type f \
   \( -name '*.avi' -o -name '*.mkv' -o -name '*.mp4' \) -print0 |

while IFS= read -r -d ''; do

    b=$(basename "$REPLY")
    c=${b::1}

    case $c in
        [a-zA-Z]) label=${c^} ;; [0-9]) label="0-9" ;; *) label="_" ;;
    esac

    [[ ! -d "$sorted/$label" ]] && mkdir -p "$sorted/$label"

    if [[ -L $sorted/$label/$b ]] || [[ -e $sorted/$label/$b ]]; then
        echo "File/link: '$b' exists, skipping."
        continue
    fi

    case $sort_type in
        link)
            ln -rfst "$sorted/$label" -- "$REPLY"
            ;;
        move)
               mv -t "$sorted/$label" -- "$REPLY"
            ;;
    esac
done
bac0n
источник
Возможно, я сделал это неправильно, но это определенно не сработало, потерял десять файлов. У меня есть тройное понимание части REPLY? в чем суть Все, что осталось (внутри папок ABCD ....) - сами файлы псевдонимов, указывающие на ... псевдоним
я калькулятор TI
REPLY устанавливается на строку ввода, читаемую встроенной командой read, если аргументы не предоставлены.
bac0n
хорошо, а затем в конце вы делаете ln -rfst "$ sorted / $ label" - "$ REPLY", зачем делать псевдоним, если мы просто переместили их с помощью mv?
Я калькулятор TI
По умолчанию он создает ссылки из каталога «videos» в ваш отсортированный каталог, если вы хотите просмотреть их, вам нужно раскомментировать «Move videos» и прокомментировать «Link videos» (я думаю, что символические ссылки менее страшны)
bac0n
... только не оба одновременно.
bac0n
2

Защита в вашем .bashrc:

alias mv="mv -n --backup=numbered"

источник
1
@Zanna, спасибо за это! Добавлены цитаты.
Несколько вопросов, просто чтобы быть уверенным (будучи новичком), добавление в файл .zshrc также допустимо (если используется ZSH)? в man mv написано -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)так, имеет ли значение, что тег -n будет перед следующими тегами? Параметр --backup = numbered создаст двойное число каждого права, не слишком ли это излишне (и потребляет много энергии) при работе с видеофайлами очень большого размера (терабайтами). Благодарность !
Я калькулятор TI
2

Для записи, некоторые способы остановить mvперезапись существующих файлов:

  • Если вы хотите перейти в каталог, добавьте косую черту к цели, т.е. используйте mv "$file" "$dir"/вместо mv "$file" "$dir". Если $dirне существует или не каталог, mvбудет жаловаться:

    $ touch a
    $ mv a z/
    mv: cannot move 'a' to 'z/': Not a directory
    $ touch z
    $ mv a z/
    mv: failed to access 'z/': Not a directory

    Похоже, это делает системный вызов rename("a", "z/"), поэтому он должен быть защищен от уязвимостей, связанных с проверкой на время использования, в случае, если кто-то обрабатывает один и тот же набор файлов в одно и то же время.

  • В качестве альтернативы используйте mv -t "$dir" "$file". Опять же, он будет жаловаться, если $dirне каталог.

  • Используйте -nпараметр, чтобы предотвратить перезапись существующих файлов:

    -n, --no-clobber
        do not overwrite an existing file

    Это не помешает ему переименовать первый файл, но не помешает другим.

    Кажется, это вызывает равнину rename(), поэтому может быть небезопасно при одновременной обработке. (Там есть renameat2()поддержка флага для предотвращения перезаписи.)

ilkkachu
источник
1

Хотя, очевидно, это не так, возможно, вы могли бы сделать это и не потерять файлы. Для этого потребуется одна из двух вещей:

  • Одна или несколько «жестких ссылок» на одни и те же файлы существуют в других местах файловой системы.
  • Один или несколько процессов имеют открытый файл

Файловые системы Unix позволяют нескольким элементам каталога обращаться к одному и тому же содержимому файла . Это называется « жесткой связью ». Вы можете создавать жесткие ссылки с помощью lnкоманды, без общей -s(мягкой / символической) опции. Пока существует хотя бы одна жесткая ссылка на содержимое файла, она не будет повторно использоваться файловой системой.

(Примечание: разрешения обычно применяются к содержимому файла, а не к записи каталога. Вот почему обычный пользователь может иногда удалять файл, принадлежащий ему root, но не записывать его. Операция удаления изменяет папку, а не сам файл. )

Файловая система также не будет повторно использовать содержимое файла, если хотя бы один процесс имеет открытый файл. Даже если нет записи в каталоге, файловая система не будет считать пространство свободным, пока ни один процесс не откроет его. Файл можно восстановить из виртуальной файловой системы /proc/<pid>/fd, rootпока файл остается открытым. (Спасибо @fluffysheap.)

wberry
источник
1
Если случается, что процесс с открытым файлом существует, вы можете восстановить его, просмотрев его в / proc / <pid> / fd. См superuser.com/questions/283102/...
fluffysheap