NB: Harap pertanggungjawabkan nama file dengan karakter aneh (mis. Spasi)
Nathan JB
3
Anda juga harus menentukan cara menangani kasus yang mungkin ada ./foo/bar/baz.pngdan ./foo-bar-baz.pngkeduanya ada. Saya berasumsi Anda tidak ingin mengganti yang terakhir dengan yang pertama?
jw013
Dalam satu kasus saya, itu tidak relevan, tetapi jawabannya memang harus menjelaskan sehingga orang lain dapat mengandalkannya! Terima kasih!
Nathan JB
Jawaban:
7
Peringatan: Saya mengetik sebagian besar perintah ini langsung di browser saya. Lektor peringatan.
Penjelasan: Pola ini **/*cocok dengan semua file di subdirektori dari direktori saat ini, secara rekursif (tidak cocok dengan file di direktori saat ini, tetapi ini tidak perlu diganti namanya). Dua pasang kurung pertama adalah grup yang dapat disebut sebagai $1dan $2dalam teks pengganti. Pasangan terakhir kurung menambahkan Dkualifikasi glob sehingga file dot tidak dihilangkan. -o -iartinya meneruskan -iopsi ke mvsehingga Anda diminta jika file yang ada akan ditimpa.
Dengan hanya alat POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Penjelasan: casepernyataan menghilangkan direktori saat ini dan subdirektori tingkat atas dari direktori saat ini. targetberisi nama file sumber ( $0) dengan ./strip utama dan semua garis miring diganti dengan tanda hubung, ditambah final z. Final zada di sana dalam kasus nama file berakhir dengan baris baru: jika tidak, substitusi perintah akan menghapusnya.
Jika Anda findtidak mendukung -exec … +(OpenBSD, saya melihat Anda):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
Dengan bash (atau ksh93), Anda tidak perlu memanggil perintah eksternal untuk mengganti garis miring dengan tanda hubung, Anda dapat menggunakan ekspansi parameter ksh93 dengan konstruksi penggantian string ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
for sourceContoh - contoh melewatkan file / dir pertama yang disajikan ... Saya akhirnya menemukan mengapa Anda (biasanya) telah menggunakan _sebagai $0:) ... dan terima kasih atas jawaban yang bagus. Saya belajar banyak dari mereka.
Peter.O
Perhatikan bahwa contoh zmv hanya menjalankan proses kering: ia mencetak perintah langkah tetapi tidak menjalankannya. Untuk benar-benar menjalankan perintah-perintah itu, hapus n di -Qn.
Peter
5
Meskipun sudah ada jawaban bagus di sini, saya menemukan ini lebih intuitif, di dalam bash:
Di mana aaadirektori Anda saat ini. File-file yang dikandungnya akan dipindahkan ke direktori saat ini (hanya menyisakan direktori kosong di aaa). Kedua panggilan untuk trmenangani direktori dan ruang (tanpa logika untuk tebas lolos - latihan untuk pembaca).
Saya menganggap ini lebih intuitif dan relatif mudah untuk di-tweak. Yaitu saya bisa mengubah findparameter jika mengatakan saya hanya ingin mencari file .png. Saya dapat mengubah trpanggilan, atau menambahkan lebih banyak sesuai kebutuhan. Dan saya dapat menambahkan echosebelumnya mvjika saya ingin memeriksa secara visual bahwa itu akan melakukan hal yang benar (kemudian ulangi tanpa tambahan echohanya jika semuanya terlihat benar:
Perhatikan juga saya menggunakan find aaa... dan tidak find .... atau find aaa/... karena dua yang terakhir akan meninggalkan artefak lucu di nama file terakhir.
Terima kasih untuk liner yang satu ini - ini bekerja dengan baik untuk saya ketika saya melakukannya dari luar direktori (yang persis seperti yang Anda katakan). Ketika saya mencoba melakukan dari dalam direktori (berharap untuk menghindari nama direktori root yang ditambahkan) semua file "menghilang". Saya telah mengubahnya menjadi file tersembunyi di direktori yang sama.
Catatan: ini tidak akan berfungsi untuk nama file yang mengandung \n.
Ini, tentu saja hanya memindahkan ffile jenis ...
Satu-satunya bentrokan nama akan dari file yang sudah ada di pwd
Ini adalah lsversi .. selamat datang komentar. Ini berfungsi untuk nama file yang aneh seperti ini
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", tetapi saya benar-benar tertarik untuk mengetahui adanya jebakan. Ini lebih merupakan latihan dalam "dapatkah difitnah lsbenar-benar melakukannya (dengan kuat)?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Ini tidak menangani nama file aneh dengan aman. Ruang akan memecahnya (antara lain).
jw013
Pada pandangan pertama, ini adalah solusi yang bersih dan mudah dibaca tetapi @ jw013 benar, itu tidak akan menangani spasi dengan baik. Apakah mengubah cpsaluran akan cp -v "$FILE" "$NEWFILE"membantu? (Juga, Anda seharusnya tidak hanya mengasumsikan file png.)
Nathan JB
2
@ NathanJ.Brauer Menambahkan kutipan ke cpbaris tidak cukup. Seluruh pendekatan findoutput parsing dengan forloop cacat. Satu-satunya cara aman untuk digunakan findadalah dengan -exec, atau -print0jika tersedia (kurang portabel dari -exec). Saya akan menyarankan alternatif yang lebih kuat tetapi pertanyaan Anda saat ini tidak ditentukan.
jw013
-2
Aman untuk spasi dalam nama file:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Berlari dengan saya cpalih - alih mvkarena alasan yang jelas, tetapi harus menghasilkan hal yang sama.
type find-> find is /usr/bin/finddi mesin saya. Menggunakan PATHsebagai nama variabel dalam sebuah skrip adalah ide yang buruk. Juga, skrip Anda manglesi nama file dengan spasi atau garis miring terbalik, karena Anda menyalahgunakan readperintah (cari situs ini IFS read -r).
Gilles 'SANGAT berhenti menjadi jahat'
find is hashed (/bin/find), relevan bagaimana? Distro yang berbeda berbeda?
Tim
Tahu aku seharusnya menjauh dari ini, umpan burung pemakan bangkai.
Tim
@Tim Anda selalu dapat menghapus jawaban untuk mendapatkan kembali perwakilan Anda (dan juga jawaban saya b / c biaya rep untuk downvote). Jalur hard-coding ke dalam skrip tampaknya menunjukkan bahwa Anda kehilangan titik PATHvariabel lingkungan, yang merupakan sesuatu yang digunakan oleh setiap sistem Unix. Jika Anda menulis /bin/findmaka skrip Anda benar-benar rusak pada setiap sistem (Gilles, milikku, dan mungkin OP) yang tidak memiliki /bin/find(misalnya b / c itu ada di /usr/bin/find). Inti dari PATHvariabel lingkungan adalah menjadikan ini bukan masalah.
jw013
By the way, $(pwd)sudah tersedia dalam variabel: $PWD. Tetapi Anda tidak boleh menggunakan jalur absolut di sini: jika skrip Anda dipanggil dari, katakanlah /home/tim, ia mencoba mengubah namanya /home/tim/some/pathmenjadi /home-tim-some-path.
./foo/bar/baz.png
dan./foo-bar-baz.png
keduanya ada. Saya berasumsi Anda tidak ingin mengganti yang terakhir dengan yang pertama?Jawaban:
Peringatan: Saya mengetik sebagian besar perintah ini langsung di browser saya. Lektor peringatan.
Dengan zsh dan zmv :
Penjelasan: Pola ini
**/*
cocok dengan semua file di subdirektori dari direktori saat ini, secara rekursif (tidak cocok dengan file di direktori saat ini, tetapi ini tidak perlu diganti namanya). Dua pasang kurung pertama adalah grup yang dapat disebut sebagai$1
dan$2
dalam teks pengganti. Pasangan terakhir kurung menambahkanD
kualifikasi glob sehingga file dot tidak dihilangkan.-o -i
artinya meneruskan-i
opsi kemv
sehingga Anda diminta jika file yang ada akan ditimpa.Dengan hanya alat POSIX:
Penjelasan:
case
pernyataan menghilangkan direktori saat ini dan subdirektori tingkat atas dari direktori saat ini.target
berisi nama file sumber ($0
) dengan./
strip utama dan semua garis miring diganti dengan tanda hubung, ditambah finalz
. Finalz
ada di sana dalam kasus nama file berakhir dengan baris baru: jika tidak, substitusi perintah akan menghapusnya.Jika Anda
find
tidak mendukung-exec … +
(OpenBSD, saya melihat Anda):Dengan bash (atau ksh93), Anda tidak perlu memanggil perintah eksternal untuk mengganti garis miring dengan tanda hubung, Anda dapat menggunakan ekspansi parameter ksh93 dengan konstruksi penggantian string
${VAR//STRING/REPLACEMENT}
:sumber
for source
Contoh - contoh melewatkan file / dir pertama yang disajikan ... Saya akhirnya menemukan mengapa Anda (biasanya) telah menggunakan_
sebagai$0
:) ... dan terima kasih atas jawaban yang bagus. Saya belajar banyak dari mereka.Meskipun sudah ada jawaban bagus di sini, saya menemukan ini lebih intuitif, di dalam
bash
:Di mana
aaa
direktori Anda saat ini. File-file yang dikandungnya akan dipindahkan ke direktori saat ini (hanya menyisakan direktori kosong di aaa). Kedua panggilan untuktr
menangani direktori dan ruang (tanpa logika untuk tebas lolos - latihan untuk pembaca).Saya menganggap ini lebih intuitif dan relatif mudah untuk di-tweak. Yaitu saya bisa mengubah
find
parameter jika mengatakan saya hanya ingin mencari file .png. Saya dapat mengubahtr
panggilan, atau menambahkan lebih banyak sesuai kebutuhan. Dan saya dapat menambahkanecho
sebelumnyamv
jika saya ingin memeriksa secara visual bahwa itu akan melakukan hal yang benar (kemudian ulangi tanpa tambahanecho
hanya jika semuanya terlihat benar:Perhatikan juga saya menggunakan
find aaa
... dan tidakfind .
... ataufind aaa/
... karena dua yang terakhir akan meninggalkan artefak lucu di nama file terakhir.sumber
Catatan: ini tidak akan berfungsi untuk nama file yang mengandung
\n
.Ini, tentu saja hanya memindahkan
f
file jenis ...Satu-satunya bentrokan nama akan dari file yang sudah ada di pwd
Diuji dengan bagian dasar ini
Yang menghasilkan
sumber
Ini adalah
ls
versi .. selamat datang komentar. Ini berfungsi untuk nama file yang aneh seperti ini"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
, tetapi saya benar-benar tertarik untuk mengetahui adanya jebakan. Ini lebih merupakan latihan dalam "dapatkah difitnahls
benar-benar melakukannya (dengan kuat)?"sumber
Cukup pipa nama file + path ke
tr
perintah.tr <replace> <with>
sumber
cp
saluran akancp -v "$FILE" "$NEWFILE"
membantu? (Juga, Anda seharusnya tidak hanya mengasumsikan file png.)cp
baris tidak cukup. Seluruh pendekatanfind
output parsing denganfor
loop cacat. Satu-satunya cara aman untuk digunakanfind
adalah dengan-exec
, atau-print0
jika tersedia (kurang portabel dari-exec
). Saya akan menyarankan alternatif yang lebih kuat tetapi pertanyaan Anda saat ini tidak ditentukan.Aman untuk spasi dalam nama file:
Berlari dengan saya
cp
alih - alihmv
karena alasan yang jelas, tetapi harus menghasilkan hal yang sama.sumber
type find
->find is /usr/bin/find
di mesin saya. MenggunakanPATH
sebagai nama variabel dalam sebuah skrip adalah ide yang buruk. Juga, skrip Anda manglesi nama file dengan spasi atau garis miring terbalik, karena Anda menyalahgunakanread
perintah (cari situs iniIFS read -r
).find is hashed (/bin/find)
, relevan bagaimana? Distro yang berbeda berbeda?PATH
variabel lingkungan, yang merupakan sesuatu yang digunakan oleh setiap sistem Unix. Jika Anda menulis/bin/find
maka skrip Anda benar-benar rusak pada setiap sistem (Gilles, milikku, dan mungkin OP) yang tidak memiliki/bin/find
(misalnya b / c itu ada di/usr/bin/find
). Inti dariPATH
variabel lingkungan adalah menjadikan ini bukan masalah.$(pwd)
sudah tersedia dalam variabel:$PWD
. Tetapi Anda tidak boleh menggunakan jalur absolut di sini: jika skrip Anda dipanggil dari, katakanlah/home/tim
, ia mencoba mengubah namanya/home/tim/some/path
menjadi/home-tim-some-path
.