Файли "перезаписані", простір все ще зайнятий, вони втрачені?


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, які менше , ніж Гб, і, судячи з нею відео SINGLE.

У загальному використанні диска самої папки 50Gb не враховується, але загальний дисковий простір комп'ютера залишився колишнім. Змушує мене думати, що файли не видаляються?

Будь-яка допомога вдячна, дякую :)

Редагувати: файлів насправді вже немає, залишається лише останній файл, який потрібно записати, на оновлення інформації про використання диску потрібен певний час. Мораль історії, запустіть сценарії на макетних файлах раніше!


3
І зробили каталоги по імені A, Bі так далі існували перед запуском сценарію? Якщо ні, ви просто перейменували файли. Усі файли, імена яких починалися з aабо Aбули перейменовані A, тому збереглися лише останній перейменований файл, інші перезаписуються. Для виклику змінної dirне створюється каталог!
mook765

2
це так, як це інтерпретували. "останній перейменований файл вижив" га. каталогів не існувало, я повинен був додати "штрих" для кожного перед рукою. дякую за уточнення
я калькулятор TI

4
+1 для ".. моралі історії, запускайте свої сценарії на макетних файлах раніше!"
sudodus

4
Порада, щоб уникнути подібних проблем: Використовуйте mv "$file" "$dir/", із заднім числом /; тоді, якщо $dirйого не існує, mvпомилка буде замість перейменування $fileна $dir. Також врахуйте mv -iі mv -n. І завжди робіть mkdir -pперед тим, як рухатись, для гарної міри.
marcelm

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

1
додавання touchбуде гарним підходом mkdir, щоб уникнути конфліктів у разі запуску сценарію кілька разів?
Я калькулятор TI

2
touchстворює файл, якщо ім'я не існує. Тож у цьому випадку це не зробить те, що ви хочете. mkdir -pможе обробляти сценарій кілька разів.
sudodus

6
Ще один простий спосіб, який можна зробити mvбільш безпечним - це ввійти в звичку додавати mv "$file" "$dir/"
кінцеву косу рису

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" : переміщення кожного файлу (або каталогу) до відповідної цілі.

Проблема з цим полягає в тому, що він також перемістить будь-які каталоги, які у вас є. Він не перемістить цільові каталоги, тому що або вони ще не існують, або ви спробуєте перенести їх у себе, але будь-які існуючі dir, які не є цільовим dir, будуть переміщені.

Якщо це проблема, вам доведеться зробити щось подібне:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done

У чому різниця між ${letter^}і ${letter^^}, якщо вони однакові, навіщо використовувати ${letter^^}замість них $dir?
Фонд позову Моніки

1
@NicHartley з ${var^}великої літери використовує лише велику літеру, а ${var^^}всі літери - з великої літери. Тут нічого не має значення, оскільки є $letterлише одна літера.
тердон

Це ідеальний відповідь, за винятком того, ви можете додати додатковий шар ретельності, додавши каталог косу риску $dirв mvкоманді. (У теперішньому вигляді він не вдасться, якщо файл існує з великим
Stig Hemmer

@StigHemmer так, так. Дуже хороший момент, дякую. Відповідь відредаговано.
тердон

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

Можливо, я зробив це неправильно, але це, безумовно, не працювало, втратив десять файлів, хай. У мене є потрійне розуміння частини REPLY? Який намір Все, що залишилося (всередині папок ABCD ....) - це самі файли псевдонімів, які вказують на .. псевдонім
я калькулятор TI

REPLY встановлюється на рядок введення, який читається командою прочитаної вбудованої програми, коли аргументи не наводяться.
bac0n

ок, а потім наприкінці ви робите ln -rfst "$ sorted / $ label" - "$ REPLY", чому робити псевдоніми, якщо ми просто перемістили їх з mv?
Я калькулятор TI

За замовчуванням він створює посилання з каталогу "відео" у вашому відсортованому каталозі, якщо ви хочете їх змінити, вам потрібно відмітити "Перемістити відео" та прокоментувати "Посилання відео" (я думаю, що посилання менш страшні)
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 = пронумеровані створить подвійні кожне право, чи не то, що трохи перебору (і простір енергії / споживання) при роботі з вилучимо великий відео файлами (мовець терабайт) є. Спасибі !
Я калькулятор 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()буде підтримувати прапор для запобігання перезапису.)


1

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

  • Одне або кілька "жорстких посилань" на ті самі файли існують в інших місцях файлової системи
  • В одному або декількох процесах відкритий файл

Файлові системи Unix дозволяють більш ніж одній записи каталогу звертатися до того самого вмісту файлу . Це називається " жорсткою ланкою ". Ви можете створити жорсткі посилання за допомогою lnкоманди, без загального -s(м'якого / символічного) параметра. Поки існує хоча б одне тверде посилання на вміст файлу, файлова система не буде повторно використана.

(Бічна примітка, дозволи зазвичай застосовуються до вмісту файлу, а не до запису каталогу. Ось чому звичайний користувач може іноді видаляти файл, що належить йому root, але не записувати до нього. Операція видалення змінює папку, а не сам файл. )

Файлова система також не повторно використовувати вміст файлу, якщо принаймні один процес відкриває файл. Навіть якщо немає запису в каталозі, файлова система не вважатиме простір вільним, поки жодні процеси не відкриють його. Файл можна відновити з віртуальної файлової системи /proc/<pid>/fdдо rootтих пір, поки файл залишається відкритим. (Дякую @fluffysheap.)


1
Якщо трапився процес із відкритим файлом, ви можете відновити його, переглянувши його в / proc / <pid> / fd. Дивіться superuser.com/questions/283102/…
fluffysheap
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.