Pliki „nadpisane”, wciąż zajęte, są zgubione?

11

Tak głupie niecierpliwe, że użyłem następującego skryptu na moim serwerze 19.04, próbując przenieść kilka plików wideo do folderów z prefiksami:

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

Nie mam pojęcia, gdzie poszło nie tak, ale zamiast przenieść pliki do folderów, przeszedł do pojedynczego wyjścia .. więc:

----> 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 <----

Na szczęście zatrzymałem proces (CTRL + C), gdy tylko zauważyłem, że nie idzie zgodnie z przeznaczeniem i nie przejrzałem całego folderu.

Więc teraz mam te pliki Ai C, które są mniej niż GB, i wygląda na to są wideo pojedynczym.

W całkowitym wykorzystaniu dysku przez sam folder nie jest uwzględnione 50 Gb, ale ogólna przestrzeń dyskowa komputera pozostała taka sama. Sprawiasz, że myślę, że pliki nie zostały usunięte?

Każda pomoc doceniona, dziękuję :)

Edycja: pliki faktycznie zniknęły, pozostaje tylko ostatni plik do zapisania, wystarczyło trochę czasu, aby zaktualizować informacje o użyciu dysku.

Jestem kalkulatorem TI
źródło
3
A czy katalogi nazwane A, Bi tak dalej istniała przed uruchomieniem skryptu? Jeśli nie, po prostu zmieniłeś nazwy plików. Wszystkie pliki, których nazwy zaczynały się od alub których nazwa Azostała zmieniona A, więc przetrwał tylko ostatni plik o zmienionej nazwie, pozostałe są zastępowane. Wywołanie zmiennej dirnie tworzy katalogu!
mook765
2
tak też to interpretował. „ostatni plik o zmienionej nazwie przetrwał” ha. katalogi nie istniały, powinienem był dodać „dotyk” do każdego z nich wcześniej. dzięki za wyjaśnienie
Jestem kalkulatorem TI
4
+1 za „.. morał historii, już wcześniej uruchamiaj skrypty na fałszywych plikach!”
sudodus
4
Wskazówka, aby uniknąć takich problemów: Użyj mv "$file" "$dir/", z końcem /; to jeśli $dirnie istnieje, mvwystąpi błąd zamiast zmiany nazwy $filena $dir. Weź również pod uwagę mv -ii mv -n. I zawsze rób to mkdir -pprzed przeprowadzką, na wszelki wypadek.
marcelm
3
@sudodus Jeszcze lepszy morał: „Zawsze wykonuj kopię zapasową danych!”.
Jon Bentley,

Odpowiedzi:

15

Myślę, że to jest problem: powinieneś utworzyć katalogi A, B, C ... Z. Jeśli tak, mvpolecenie powinno przenieść pliki do tych katalogów.

Ale jeśli nie, mvpolecenie przenosi pliki do plików o tych nazwach, A, B, C ... i myślę, że to właśnie zrobiłeś.

Aby uczynić shellscript bezpieczniejszym, powinieneś stworzyć katalogi (jeśli jeszcze ich nie ma) przed rozpoczęciem przenoszenia.

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

Jeśli chcesz, aby wszystko stało się jeszcze bezpieczniejsze, możesz również użyć mvtej -iopcji

   -i, --interactive
          prompt before overwrite
sudodus
źródło
1
czy dodanie touchbyłoby dobrym zamiennikiem, aby mkdiruniknąć konfliktów w przypadku wielokrotnego uruchamiania skryptu?
Jestem kalkulatorem TI,
2
touchtworzy plik, jeśli nazwa nie istnieje. Więc nie zrobi tego, co chcesz w tym przypadku. mkdir -pporadzi sobie z użyciem skryptu kilka razy.
sudodus
6
Innym prostym sposobem można zrobić mvbezpieczniej jest dostać w zwyczaju dodanie ukośnika do nazwy docelowej, gdy cel jest katalogiem czylimv "$file" "$dir/"
steeldriver
7

@ Sudodus już wyjaśnił, co poszło nie tak, ale tutaj jest następna prostsza wersja skryptu na następny raz:

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

Wyjaśnienie

  • for letter in {a..z}; do: {a..z}rozwija się do wszystkich małych liter między ai 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

    Spowoduje to iterację wszystkich małych liter, zapisując każdą z nich jako $letter.

  • dir=${letter^}: składnia ${var^^}zwraca zawartość zmiennej $varz pierwszym znakiem wielkimi literami (ponieważ ma tylko jeden znak, to wszystko, czego potrzebujemy). Tak więc, jeśli $letterjest a, to ${letter^^}jest A, a zatem $dirbędzie wersją bieżącej dużej litery $letter.

  • mkdir -p -- "$dir": utwórz katalog. Jeśli już istnieje, nie rób nic ( -p). -- Oznacza koniec opcji i jest przydatna do ochrony przed nazwach zaczynających się -.
  • mv -- "$letter"* "${letter^}"* "$dir" : przenieś każdy plik (lub katalog) do odpowiedniego celu.

Problem polega na tym, że przenosi także wszystkie katalogi, które możesz mieć. Nie przeniesie katalogów docelowych, ponieważ albo jeszcze nie istnieją, albo spróbujesz przenieść je do siebie, ale wszelkie istniejące katalogi, które nie są katalogiem docelowym, zostaną przeniesione.

Jeśli to jest problem, będziesz musiał zrobić coś takiego:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
terdon
źródło
Jaka jest różnica między ${letter^}i ${letter^^}, a jeśli są identyczne, to po co używać ${letter^^}zamiast $dir?
Pozew funduszu Moniki,
1
@NicHartley pisze ${var^}wielką literą tylko pierwszą literę, a ${var^^}wszystkie wielkie litery. Nie ma tu znaczenia, ponieważ $letterma tylko jedną literę.
terdon
Jest to idealne rozwiązanie, chyba warto dodać dodatkową warstwę starannością przez dodanie ukośnika do katalogu $dirw mvpoleceniu. (W obecnej formie nie powiedzie się, jeśli plik istnieje z pojedynczą wielką literą)
Stig Hemmer,
@StigHemmer ups, tak rzeczywiście. Bardzo dobry punkt, dzięki. Odpowiedź edytowana.
terdon
4

Zamiast sprawdzać każdy plik w tablicy słownikowej, która tworzy dużo iteracji, możesz dopasować pliki do wzorców.

Bardzo podstawowy rodzaj:

#!/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
źródło
Może zrobiłem to źle, ale to zdecydowanie nie działało, straciłem dziesięć plików lol. Mam podwójne rozumienie części ODPOWIEDZI? jaki jest cel Wszystko, co pozostało (w folderach ABCD ....) to same pliki aliasów wskazujące na… ten alias
Jestem kalkulatorem TI
REPLY ustawione na linię wejściową odczytaną przez wbudowane polecenie odczytu, gdy nie podano argumentów.
bac0n
ok, a potem na końcu robisz w -rfst „$ sorted / $ label” - „$ REPLY” dlaczego alias, skoro właśnie przenieśliśmy je za pomocą mv?
Jestem kalkulatorem TI,
Domyślnie tworzy linki z „filmy” w swoim katalogu posortowanych katalogu, jeśli chcesz je trzeba Odkomentuj filmy „Move” i komentować filmy „Link” mV (myślę dowiązania są mniej przerażające)
bac0n
... po prostu nie oba jednocześnie.
bac0n
2

Zabezpiecz w swoim .bashrc:

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

źródło
1
@Zanna, dzięki za to! Dodano cytaty.
Kilka pytań, aby się upewnić (będąc nowicjuszem), dodanie pliku .zshrc jest również poprawne (jeśli używasz ZSH)? w man mv jest napisane, -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)więc czy to ważne, że tag -n będzie przed następującymi tagami? W --backup = numerowane będą tworzyć podwójne każdego prawa, a nie, że trochę przesadą (przestrzeń i energia / czasochłonne) gdy ma do czynienia z plikami wideo uber-duży (rozmawiających terabajtów) jest. Dzięki !
Jestem kalkulatorem TI,
2

Dla przypomnienia, kilka sposobów, aby przestać mvzastępować istniejące pliki:

  • Jeśli chcesz przejść do katalogu, dodaj ukośnik do celu, tzn. Użyj mv "$file" "$dir"/zamiast mv "$file" "$dir". Jeśli $dirnie istnieje lub nie jest katalogiem, mvnarzeka:

    $ 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

    Wydaje się, że wywołuje rename("a", "z/")to wywołanie systemowe , więc powinno być bezpieczne od luk w zabezpieczeniach od czasu sprawdzenia do czasu użycia, na wypadek, gdyby ktoś obsługiwał ten sam zestaw plików w tym samym czasie.

  • Alternatywnie użyj mv -t "$dir" "$file". Znów będzie narzekać, jeśli $dirnie jest katalogiem.

  • Użyj -nopcji, aby zapobiec zastąpieniu istniejących plików:

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

    Nie powstrzyma go przed zmianą nazwy pierwszego pliku, ale nie usunie go z innymi.

    To wydaje się nazwać zwykłym rename(), więc może nie być bezpieczne przy jednoczesnej obsłudze. (Jest renameat2()taki, który obsługiwałby flagę, aby zapobiec zastąpieniu).

ilkkachu
źródło
1

Chociaż najwyraźniej tak nie jest, możliwe, że możesz to zrobić i nie stracić plików. Wymagałoby to jednej z dwóch rzeczy:

  • Co najmniej jedno „twarde łącze” do tych samych plików istnieje w innym miejscu w systemie plików
  • Jeden lub więcej procesów ma otwarty plik

Uniksowe systemy plików pozwalają, aby więcej niż jedna pozycja katalogu odnosiła się do dokładnie tej samej zawartości pliku . Nazywa się to „ twardym linkiem ”. Możesz tworzyć twarde linki za pomocą lnpolecenia, bez opcji common -s(soft / symbolic). Tak długo, jak istnieje przynajmniej jedno twarde łącze do zawartości pliku, nie będzie ono ponownie wykorzystywane przez system plików.

(Uwaga dodatkowa: uprawnienia zwykle dotyczą zawartości pliku, a nie pozycji katalogu. Dlatego zwykły użytkownik może czasami usunąć plik będący własnością root, ale nie może do niego zapisać. Operacja usuwania modyfikuje folder, a nie sam plik. )

System plików również nie będzie ponownie używał zawartości pliku, dopóki przynajmniej jeden proces ma otwarty plik. Nawet jeśli nie istnieje pozycja katalogu, system plików nie uzna, że ​​miejsce jest wolne, dopóki żadne procesy nie zostaną otwarte. Plik można odzyskać z wirtualnego systemu plików /proc/<pid>/fd, rootdopóki plik jest otwarty. (Dzięki @fluffysheap.)

jagoda
źródło
1
Jeśli zdarzy się proces z otwartym plikiem, możesz go odzyskać, wyszukując go w / proc / <pid> / fd. Zobacz superuser.com/questions/283102/…
fluffysheap