File 'Timpa', ruang masih ditempati, apakah hilang?

11

Jadi, saya sangat tidak sabaran menggunakan skrip berikut di server 19,04 saya dalam upaya untuk memindahkan banyak file video ke folder dengan awalan:

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

Tidak ada petunjuk di mana ia salah, tetapi alih-alih memindahkan file ke folder, ia pergi ke output tunggal .. jadi:

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

Untungnya saya menghentikan proses (CTRL + C) segera setelah saya perhatikan itu tidak berjalan seperti yang dimaksudkan dan tidak pergi melalui seluruh folder.

Jadi sekarang saya punya file-file itu Adan C, yang kurang dari Gb, dan sepertinya itu adalah video TUNGGAL.

Ada 50Gb yang tidak terhitung dalam penggunaan total disk folder itu sendiri, tetapi ruang disk keseluruhan komputer tetap sama. Membuat saya berpikir file tidak terhapus?

Setiap bantuan dihargai, terima kasih :)

Sunting: file-file tersebut benar-benar hilang, hanya file terakhir yang tersisa, semua yang diperlukan adalah beberapa waktu untuk disk menggunakan info untuk memperbarui .. moral dari cerita, jalankan skrip Anda pada file tiruan sebelumnya!

Saya seorang kalkulator TI
sumber
3
Dan apakah direktori dinamai A, Bdan seterusnya ada sebelum menjalankan skrip? Jika tidak, Anda baru saja mengganti nama file. Semua file yang namanya dimulai aatau Adiubah namanya A, sehingga hanya file yang diganti namanya terakhir yang bertahan, yang lain ditimpa. Untuk memanggil variabel dirtidak membuat direktori!
mook765
2
Itulah cara menafsirkannya juga. "file berganti nama terakhir selamat" ha. direktori tidak ada, saya harus menambahkan 'sentuhan' untuk masing-masing sebelumnya. terima kasih telah menjelaskan
Saya kalkulator TI
4
Beri +1 untuk ".. pesan moral, jalankan skrip Anda pada file tiruan sebelumnya!"
sudodus
4
Kiat untuk menghindari masalah seperti ini: Gunakan mv "$file" "$dir/", dengan trailing /; maka jika $dirtidak ada, mvakan kesalahan bukannya ganti nama $filemenjadi $dir. Juga pertimbangkan mv -idan mv -n. Dan selalu lakukan mkdir -psebelum bergerak, untuk ukuran yang baik.
marcelm
3
@sudodus Bahkan moral yang lebih baik, "Selalu buat cadangan data Anda!".
Jon Bentley

Jawaban:

15

Saya pikir ini masalahnya: Anda seharusnya membuat direktori A, B, C ... Z. Jika Anda melakukannya, mvperintahnya harus memindahkan file ke direktori tersebut.

Tetapi jika tidak, mvperintah memindahkan file ke file dengan nama-nama itu, A, B, C ... dan saya pikir ini yang Anda lakukan.

Untuk membuat shellscript lebih aman, Anda harus membuatnya membuat direktori (jika belum ada) sebelum Anda mulai bergerak.

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

Jika Anda ingin hal untuk mendapatkan bahkan lebih aman, Anda juga dapat menggunakan mvdengan -iopsi

   -i, --interactive
          prompt before overwrite
sudodus
sumber
1
apakah menambahkan touchakan menjadi pengganti yang baik mkdiruntuk menghindari konflik jika menjalankan skrip beberapa kali?
Saya kalkulator TI
2
touchmembuat file jika nama tidak ada. Jadi tidak akan melakukan apa yang Anda inginkan dalam hal ini. mkdir -pdapat menangani menggunakan skrip beberapa kali.
sudodus
6
Cara sederhana lain yang dapat Anda lakukan agar mvlebih aman adalah dengan membiasakan menambahkan garis miring pada nama target ketika targetnya adalah direktori yaitumv "$file" "$dir/"
steeldriver
7

@Sudodus sudah menjelaskan apa yang salah, tapi inilah versi skrip Anda yang lebih sederhana untuk kali berikutnya:

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

Penjelasan

  • for letter in {a..z}; do: {a..z}meluaskan ke semua huruf kecil antara adan 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

    Jadi ini akan mengulangi semua huruf kecil, menyimpan masing-masing sebagai $letter.

  • dir=${letter^}: sintaks ${var^^}mengembalikan konten variabel $vardengan karakter pertama dalam huruf besar (karena ini hanya memiliki satu karakter, itu saja yang kita butuhkan). Jadi, jika $letterini a, maka ${letter^^}adalah A, dan karena itu $dirakan menjadi versi huruf dari saat ini $letter.

  • mkdir -p -- "$dir": buat direktori. Jika sudah ada, jangan lakukan apa-apa ( -p). The -- menandakan akhir pilihan , dan berguna untuk melindungi nama dimulai dengan -.
  • mv -- "$letter"* "${letter^}"* "$dir" : pindahkan setiap file (atau direktori) ke target yang relevan.

Masalahnya, ini juga akan memindahkan direktori yang mungkin Anda miliki. Itu tidak akan memindahkan direktori target, karena mereka belum ada, atau Anda akan mencoba memindahkannya ke direktori mereka sendiri, tetapi direktori yang ada yang bukan direktori target akan dipindahkan.

Jika itu masalah, Anda harus melakukan sesuatu seperti ini:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
terdon
sumber
Apa perbedaan antara ${letter^}dan ${letter^^}, dan jika keduanya identik, mengapa digunakan ${letter^^}sebagai ganti $dir?
Gugatan Dana Monica
1
@NicHartley ${var^}hanya menggunakan huruf besar untuk huruf pertama, sedangkan ${var^^}huruf besar untuk semua huruf. Tidak ada bedanya di sini karena $letterhanya memiliki satu huruf.
terdon
Ini adalah jawaban yang sempurna, kecuali Anda mungkin ingin menambahkan lapisan ekstra kehati-hatian dengan menambahkan slash direktori $dirpada mvperintah. (Dalam bentuknya sekarang ini akan gagal jika file sudah ada sebelumnya dengan nama huruf tunggal)
Stig Hemmer
@StigHemmer ups, ya memang. Poin yang sangat bagus, terima kasih. Jawaban diedit.
terdon
4

Alih-alih memeriksa setiap file terhadap array kamus yang menciptakan banyak iterasi Anda dapat mencocokkan file dengan pola.

Sortir yang sangat mendasar:

#!/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
sumber
Mungkin saya salah tetapi itu pasti tidak berhasil, kehilangan sepuluh file lol. Saya mengalami kesulitan menggandakan bagian REPLY? apa maksudnya Yang tersisa (di dalam folder ABCD ....) adalah file alias yang menunjuk ke .. tha alias
Saya kalkulator TI
REPLY diatur ke jalur input yang dibaca oleh perintah read builtin ketika tidak ada argumen yang diberikan.
bac0n
ok dan kemudian pada akhirnya Anda lakukan ln -rfst "$ sort / $ label" - "$ REPLY" mengapa melakukan alias jika kita baru saja memindahkannya dengan mv?
Saya kalkulator TI
Secara default ia membuat tautan dari direktori "video" ke dalam direktori yang diurutkan, jika Anda ingin memutarnya, Anda harus menghapus tanda komentar "Pindahkan video" dan komentar "Tautkan video" (saya pikir symlink kurang menakutkan)
bac0n
... tidak hanya keduanya sekaligus.
bac0n
2

Lindungi di .bashrc Anda:

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

sumber
1
@Zanna, Terima kasih untuk itu! Kutipan ditambahkan.
Beberapa pertanyaan hanya untuk memastikan (menjadi pemula), menambahkan file .zshrc juga berlaku (jika menggunakan ZSH)? dalam man mv dikatakan -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)demikian, tidak jadi masalah bahwa tag -n adalah sebelum mengikuti tag? The = --backup bernomor akan membuat ganda masing-masing benar, bukankah itu (terabyte berbicara) agak berlebihan (dan ruang energi / memakan) ketika berhadapan dengan file video uber-besar. Terima kasih!
Saya kalkulator TI
2

Sebagai catatan, beberapa cara untuk berhenti mvmenimpa file yang ada:

  • Jika Anda ingin pindah ke direktori, tambahkan garis miring ke target, yaitu gunakan mv "$file" "$dir"/alih-alih mv "$file" "$dir". Jika $dirtidak ada atau tidak direktori, mvakan mengeluh:

    $ 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

    Ini sepertinya membuat system call rename("a", "z/"), jadi itu harus aman dari kerentanan waktu-ke-waktu-ke-waktu-penggunaan, jika seseorang menangani set file yang sama pada waktu yang sama.

  • Atau, gunakan mv -t "$dir" "$file". Sekali lagi, itu akan mengeluh jika $dirbukan direktori.

  • Gunakan -nopsi untuk mencegah menimpa file yang ada:

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

    Itu tidak akan menghentikannya mengubah nama file pertama, tetapi kemudian tidak akan membuangnya dengan yang lain.

    Sepertinya ini panggilan biasa rename(), jadi mungkin tidak aman dengan penanganan simultan. (Ada renameat2()yang akan mendukung bendera untuk mencegah penulisan ulang.)

ilkkachu
sumber
1

Meskipun tampaknya tidak demikian bagi Anda, ada kemungkinan Anda bisa melakukan ini dan tidak kehilangan file. Ini akan membutuhkan satu dari dua hal untuk menjadi kenyataan:

  • Satu atau lebih 'tautan keras' ke file yang sama ada di tempat lain di sistem file
  • Satu atau lebih proses membuka file

Sistem file Unix memungkinkan lebih dari satu entri direktori untuk merujuk ke konten file yang sama persis . Ini disebut ' tautan keras '. Anda dapat membuat tautan keras dengan lnperintah, tanpa opsi umum -s(lunak / simbolik). Selama setidaknya ada satu tautan keras ke konten file, itu tidak akan digunakan kembali oleh sistem file.

(Catatan samping, izin biasanya berlaku untuk konten file, bukan ke entri direktori. Inilah sebabnya mengapa pengguna biasa terkadang dapat menghapus file yang dimiliki oleh root, tetapi tidak menulisnya. Operasi penghapusan memodifikasi folder, bukan file itu sendiri. )

Sistem file juga tidak akan menggunakan kembali konten file selama setidaknya satu proses membuka file. Bahkan jika tidak ada entri direktori, sistem file tidak akan menganggap ruang menjadi bebas sampai tidak ada proses yang terbuka. File dapat dipulihkan dari virtual filesystem /proc/<pid>/fdoleh rootasalkan file tersebut tetap terbuka. (Terima kasih @fluffysheap.)

wberry
sumber
1
Jika kebetulan ada proses dengan file terbuka, maka Anda dapat memulihkannya dengan mencarinya di / proc / <pid> / fd. Lihat superuser.com/questions/283102/…
fluffysheap