Jawaban:
Semua direktori pada satu level, atau secara rekursif?
Pada satu tingkat:
autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'
Secara rekursif:
zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'
Penjelasan: zmv
mengganti nama file yang cocok dengan pola sesuai dengan teks pengganti yang diberikan. -o-i
melewati -i
opsi untuk setiap mv
perintah di bawah tenda (lihat di bawah). Dalam teks pengganti, $1
, $2
, dll, adalah kelompok kurung berturut-turut dalam pola. **
berarti semua (sub) * direktori, secara rekursif. Final (/)
bukan grup kurung tetapi kualifikasi global yang berarti hanya cocok dengan direktori. ${2:l}
dikonversi $2
ke huruf kecil.
Pada satu tingkat:
for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done
Final /
membatasi pencocokan ke direktori, dan mv -i
membuatnya meminta konfirmasi jika terjadi tabrakan. Hapus -i
untuk menimpa jika terjadi tabrakan, dan gunakan yes n | for …
. untuk tidak diminta dan tidak melakukan penggantian nama yang akan bertabrakan.
Secara rekursif:
find root/* -depth -type d -exec sh -c '
t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
[ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;
Penggunaan -depth
memastikan bahwa direktori yang sangat bersarang diproses sebelum leluhur mereka. Pemrosesan nama bergantung pada ada /
; jika Anda ingin memanggil operasi di direktori saat ini, gunakan ./*
(mengadaptasi skrip shell untuk mengatasi .
atau*
dibiarkan sebagai latihan untuk pembaca).
Di sini saya menggunakan skrip rename Perl yang dikirimkan oleh Debian dan Ubuntu /usr/bin/prename
(biasanya juga tersedia rename
). Pada satu tingkat:
rename 's!/([^/]*/?)$!\L/$1!' root/*/
Secara rekursif, dengan bash ≥4 atau zsh:
shopt -s globstar # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/
Secara rekursif, mudah dibawa:
find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +
-execdir
yang luar biasa: unix.stackexchange.com/questions/5412/... Saya kemudian menemukan bahwa ia memiliki beberapa PATH
kegilaan dan sedih :-(
Tidak ada satu perintah pun yang akan melakukan itu, tetapi Anda dapat melakukan sesuatu seperti ini:
for fd in */; do
#get lower case version
fd_lower=$(printf %s "$fd" | tr A-Z a-z)
#if it wasn't already lowercase, move it.
[ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"
done
Jika Anda ingin menjadi kuat, Anda harus memperhitungkan ketika sudah ada dua direktori yang hanya berbeda dalam kasus.
Sebagai one-liner:
for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done
for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
find -type d
, tetapi tidak bisa mendapatkannya
for fd in */;
, sehingga menghindari perlunya memeriksa apakah itu direktori, tapi saya tidak tahu apakah insting ini bagus.
tr
sudah mengharapkan suatu range, sehingga tr '[A-Z]' '[a-z]'
diterjemahkan [
ke [
dan ]
untuk ]
di lewat. Ini tidak berguna tetapi tidak berbahaya; namun tanpa tanda kutip shell akan memperluas tanda kurung jika ada file dengan nama satu huruf besar di direktori saat ini.
Saya menganggap ini sebagai tantangan satu-lapis :) Pertama, buat uji kasus:
$ for d in foo Bar eVe; do mkdir -p dcthis/$d; touch dcthis/a${d}.txt; done
$ ls dcthis/
Bar aBar.txt aeVe.txt afoo.txt eVe foo
Saya menggunakan find
untuk melihat direktori dengan huruf besar dan kemudian menurunkannya . Tebak menggunakan agak hack-ish, tapi kepalaku selalu meledak ketika aku mencoba melarikan diri secara langsung.sh -c 'mv {}
echo {} | tr [:upper:] [:lower:]
'sh -c
find
$ (cd dcthis && find . -maxdepth 1 -type d -path '*[A-Z]*' -exec sh -c 'mv {} `echo {} | tr [:upper:] [:lower:]`' \;)
$ ls dcthis/
aBar.txt aeVe.txt afoo.txt bar eve foo
Berhati-hatilah: Solusi ini tidak memeriksa apakah downcasing mengarah ke tabrakan!
mv -i
. Masalah yang lebih besar adalah bahwa Anda belum menggunakan kutipan yang tepat, sehingga perintah Anda akan gagal jika ada karakter khusus (spasi putih atau \[*?
) di mana saja dalam nama. Tidak ada gunanya menggunakan find
kecuali berulang, dan kemudian Anda perlu find -depth
, dan -path
harus -name
. Lihat jawaban saya untuk contoh kerja.
mv -i
setelah menulis ini. Poin yang bagus dengan kutipan ...
find -execdir
| ganti nama
Ini akan menjadi cara terbaik untuk melakukannya jika bukan karena kegilaan jalur relatif, karena ia menghindari Perl regex fu untuk hanya bertindak berdasarkan nama:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;
-execdir
pertama-tama cd
masuk ke direktori sebelum hanya mengeksekusi pada nama samaran.
Sayangnya, saya tidak dapat menyingkirkan PATH
bagian peretasan itu, find -execdir
menolak melakukan apa pun jika Anda memiliki jalur relatif di PATH
...: /ubuntu/621132/why-using-the-execdir-action- is-insecure-for-direktori-which-is-in-the-path / 1109378 # 1109378
mv -i a a
beri "mv: ganti nama a menjadi a / a: Argumen tidak valid".