Saya BENCI spasi dalam nama file

61

Sederhana saja. Saya tidak tahan ketika orang menggunakan spasi saat menamai file. Kadang-kadang merusak perintah konsol dan membuat output ls jelek.

Tantangannya adalah menulis sebuah program (hanya karakter ascii) yang

  1. mengganti nama semua file (termasuk direktori) di direktori saat ini ke versi dengan spasi dihapus atau diganti dengan '_'
  2. pada tabrakan, Anda perlu menambahkan pengidentifikasi unik (terserah Anda)
  3. turun secara rekursif ke semua subdirektori

Anda dapat mengasumsikan nama jalur bergaya UNIX. Lagi pula, siapa yang butuh program ini di mesin Windows?

Ini adalah kode golf, program terpendek menang (karakter #ascii). Karena saya sangat membenci ruang, setiap ruang harus dihitung dua kali.

Harap berikan bahasa, skor, program, dan deskripsi singkat tentang cara menjalankannya.

Program harus mengkompilasi dan mengeksekusi dengan upaya yang wajar pada mesin linux saya.

EDIT: Saat Etan meminta struktur file untuk pengujian, berikut ini skrip yang saya gunakan saat ini untuk membuat pohon file yang sesuai:

#!/bin/bash
rm -r TestDir

touchfiles()
{
    touch my_file
    touch my__file
    touch "my file"
    touch "my  file"
    touch " my_file  "
}

mkdir TestDir
cd TestDir

touchfiles

for dir in "Test Sub" Test_Sub "Te stSub" Te_stSub
do
    mkdir "$dir"
    cd "$dir"
    touchfiles
    cd ..
done
M.Herzkamp
sumber
22
Ini memohon solusi yang dibuat tanpa ascii chars.
Dennis Jaheruddin
50
Sekarang saya ingin belajar Whitespace
BrunoJ
10
@ BrunoJ melakukan ini di Whitespace pertama-tama akan meminta Anda untuk mengembangkan sistem akses file di WS. Saya pikir itu akan lebih menantang daripada tantangan yang sebenarnya.
Nzall
7
Menunggu seseorang untuk memposting solusi C / C ++ sehingga saya dapat mencurinya, mengkompilasi, memposting di hex sebagai kode mesin x86 dengan spasi NOL! [atau mungkin base64]
Mark K Cowan
10
Aku benci menggarisbawahi dalam nama file. Gunakan tanda hubung.
Dr. Rebmu

Jawaban:

10

Zsh + GNU coreutils - 48 byte (1 spasi)

for x   (**/*(Dod))mv   -T  --b=t   $x  $x:h/${${x:t}// }

Sungguh aneh bahwa Anda membenci ruang (ASCII) tetapi baik-baik saja dengan tab dan baris baru, tapi saya kira itu membutuhkan semua jenis.

zmv memecahkan banyak masalah pengubahan nama file secara ringkas (dan hanya sedikit tidak jelas). Namun, itu menegaskan bahwa target itu unik; sementara Anda dapat dengan mudah menambahkan sufiks unik, menambahkan sufiks hanya jika dibutuhkan cukup banyak membutuhkan pengerjaan ulang semua pekerjaan. Jadi alih-alih saya mengulang secara manual dan bergantung pada GNU mv untuk menambahkan pengidentifikasi unik jika terjadi tabrakan ( --backupopsi, ditambah --no-target-directoryjika target adalah direktori yang ada, karena jika tidak mvakan memindahkan sumber di dalam direktori itu).

(od)adalah kualifikasi global untuk mengurutkan output dengan direktori yang muncul setelah konten mereka (seperti find -depth). Dtermasuk file dot di glob. :hdan :tyang pengubah sejarah mirip dengan dirnamedan basename.

mvmengeluh bahwa itu dipanggil untuk mengganti nama file untuk diri mereka sendiri, karena glob termasuk nama file tanpa spasi. C'est la vie.

Versi tidak disatukan:

for x in **/*\ *(Dod); do
  mv --no-target-directory --backup=numbered $x ${x:h}/${${x:t}// /}
done
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
ini tidak mengubah nama file saya sama sekali!
M.Herzkamp
@ M.Herzkamp Oh, benar, zmvmengebom sebelum mvmemiliki kesempatan untuk menyelesaikan tabrakan. Ok, saya melakukan ini secara manual. Ternyata panjangnya persis sama jika saya melewatkan file dot dan bahkan menyimpan karakter jika tidak.
Gilles 'SANGAT berhenti menjadi jahat'
1
Sekarang berhasil. Btw: Saya memasukkan penalti ruang pada saat saya benar-benar menaruh dendam terhadap ruang;) Ironisnya, saya tidak mengecualikan spasi ketika saya memposting tantangan: P
M.Herzkamp
13

Bash 116 byte, 16 spasi

find . -depth -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// /_}"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;

Saya tidak menekan kesalahan untuk mendapatkan beberapa byte lagi. Ini tidak akan memiliki tabrakan.

Jika GNU non-posix finddapat diharapkan, ini dapat dipersingkat lebih lanjut:

Bash 110 byte, 15 spasi

find -d -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// /_}"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;

Menghapus spasi alih-alih menggantinya menggunakan dua byte lebih sedikit:

Bash 108 byte, 15 spasi

find -d -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// }"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;

Catatan: jika tab dapat digunakan sebagai ganti spasi, hanya 1 spasi yang diperlukan (yang ada dalam aturan kecocokan untuk substitusi di baris 2).

Terima kasih kepada Dennis untuk menemukan bug pada penawaran ganda (dan memberikan solusi)

pqnet
sumber
11
APAKAH RUANG EKSTRA DI BALIK MENEMUKAN SAYA UNTUK MENGEJUTKAN SAYA ??? ;-)
M.Herzkamp
@ M.Herzkamp Saya pikir itu adalah kesalahan salin dan tempel, tetapi sebenarnya ada di sana. Kira saya mendapat 2 poin lagi. Selain itu, -depthdalam GNU dapat digantikan oleh -d, meskipun mengeluh bahwa itu sudah usang. Saya tidak tahu tentang aturan golf, bisakah saya melakukannya?
pqnet
2
Selama itu berhasil, saya mengizinkannya. Jika penghentian menjadi penghapusan dalam versi masa depan, saya mungkin harus kembali ke jawaban ini dan menurunkannya karena tidak benar ;-)
M.Herzkamp
2
Ini tidak akan berfungsi dengan baik jika ada nama file yang berisi penawaran ganda. Untuk mengatasinya, Anda bisa menggunakan bash -c 'B=${0##*/}...' {} \;yang sebenarnya lebih pendek.
Dennis
3
Saya kira saya akan menjadi pria itu, ada apa dengan Nvariabel? Tidak pernah didefinisikan ...
Steven Penny
9

Python 180 byte

from    os  import*
t,c,h='.',chdir,path
def g(p):
    c(p)
    for x   in  listdir(t):
        if h.isdir(x):g(x)
        n=x.replace(' ','')
        while h.exists(n):n+=t
        if' 'in x:rename(x,n)
    c(t*2)
g(t)

hanya 2 spasi jika Anda menggunakan tab untuk indentasi :-)

Emanuele Paolini
sumber
Saya kira sebagian besar jawaban lain dapat meningkatkan skor mereka dengan menggunakan tab alih-alih spasi juga.
kasperd
Tapi kiriman Anda menggunakan spasi bukan? (+1 untuk kode kerja)
M.Herzkamp
Saya tidak tahu bagaimana menampilkan karakter tab dalam jawabannya ...
Emanuele Paolini
2
diganti dengan tab :-)
Emanuele Paolini
3
Betapa jeleknya ... Yah, kurasa aku memintanya :(
M.Herzkamp
5

Jika urutan sufiks file bertabrakan tidak perlu memberikan preseden ke file yang sudah ada maka yang berikut berfungsi untuk saya:

bash / find / mv 84 byte, 16 spasi

find -depth -execdir bash -c '[ "${0//[^ ]}" ] && mv -{T,--b=t} "$0" "${0// }"' {} \;

bash / find / mv 82 byte, 14 spasi

find -depth -execdir bash -c '[ "${0//[^ ]}" ]&&mv -{T,-b=t} "$0" "${0// }"' {} \;

Cuddled &&untuk menghemat dua byte ruang.

bash / find / mv 60 byte, 11 spasi

find -d -execdir bash -c 'mv -{T,-b=t} "$0" "${0// }"' {} \;

Menjatuhkan perlindungan kesalahan sehingga mendapat kesalahan dari mv pada file yang tidak memiliki ruang untuk memulai.

Sunting: Menjatuhkan tanda kutip dari yang {}diingatkan oleh Dennis. Juga diizinkan finduntuk berteriak tentang portabilitas dan penghinaan dalam versi terpendek di mana mvsudah berteriak tentang memindahkan file di atas itu sendiri.

Sunting 2: Ditambahkan -Tke mvperintah untuk menghindari direktori bersarang alih-alih mengganti nama seperti yang ditunjukkan oleh pqnet. Perluasan brace bekas dengan biaya satu karakter lebih dari hanya menggunakan satu ruang.

Etan Reisner
sumber
Anda dapat menggunakan -dalih-alih -depthdan Anda tidak perlu mengutip {}.
Dennis
@ Dennis Ya. Saya melihat -dpercakapan pada jawaban pqnet tetapi berpikir karena saya membungkam mvteriakan saya menghindari findteriakan. Meskipun saya mungkin harus mempersingkat untuk yang berteriak. Dan ya, saya selalu mengutip {}untuk beberapa alasan meskipun saya tahu Anda tidak perlu melakukannya. Kekuatan kebiasaan kurasa.
Etan Reisner
1
Ketika tabrakan terjadi pada nama direktori, itu akan menempatkan satu ke yang lain (dan bukan menghapus spasi). Gunakan -Topsi untuk mvmenghindari ini
pqnet
Ini berhasil, dan saya katakan dalam tantangan bahwa lampirannya terserah Anda. +1
M.Herzkamp
4

NodeJS - 209 byte, 3 spasi putih

s=require('fs');function a(d){s.readdirSync(d).forEach(function(f){f=d+'/'+f;i=0;r=f;if(/ /.test(f)){r=f.replace(' ','');while(s.existsSync(r))r+=i++;s.renameSync(f,r)}s.statSync(r).isDirectory()&&a(r)})}a('.');
cPu1
sumber
Saya tidak terbiasa dengan node.js. Bagaimana saya menjalankannya?
M.Herzkamp
Anda akan memerlukan simpuljs yang dapat dieksekusi Node ; simpan dalam file dan jalankannode file.js
cPu1
7
Saya mendapat pengecualian TypeError: Object #<Object> has no method 'exists'. Coba tebak: ada di baris 1! : D
M.Herzkamp
Saya sudah mengujinya. Lagi pula, saya mengganti ada dengan mitra sinkronnya. Bisakah kamu coba sekarang?
cPu1
1
Saya hanya menginstal versi 0.6.12. Itu mungkin masalahnya.
M.Herzkamp
2

Bash - 86 byte

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done
Subbeh
sumber
Ups, akan lihat
Subbeh
2
Juga, spasi dihitung dua kali ;-)
M.Herzkamp
apa sebenarnya yang Anda maksud dengan spasi dihitung dua kali?
Subbeh
1
Anda dapat menyimpan banyak karakter dengan menyingkat --backupmenjadi--b
1
Ya, sekarang ini berfungsi dengan set tes saya juga! +1
M.Herzkamp
2

Bash + Perl rename64

( renameadalah skrip Perl pada Debian dan turunannya, bukan perintah util-linux.)

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;
german_guy
sumber
11
Apa yang terjadi jika "my file.txt" dan "my_file.txt" keduanya ada?
M.Herzkamp
1
Oh benar .. Mengerjakannya segera
german_guy
1
*seharusnya {}, karena ini hanya mengubah nama file yang namanya muncul di direktori saat ini. Ini tidak menambahkan akhiran jika terjadi tabrakan. Anda dapat menyimpan sedikit dengan menghilangkan -name "* *"karena renamediam-diam mengabaikan file yang namanya tidak diubah.
Gilles 'SANGAT berhenti menjadi jahat'
2

POSIX sh+ GNU find+ GNU mv67 ASCII byte + satu (literal) ruang

find    -d  -exec   sh  -cf 'IFS=\ ;IFS=_   set $0;mv   --b=t   "$0"    "$*"'   {}  \;

Saya tidak tahu apakah itu cocok, tetapi dengan urutan ruang apa pun ini disingkirkan menjadi satu _- saya tetap menyukainya. Sebenarnya setiap urutan selain ruang terdepan / tertinggal - yang secara otomatis terpotong (yang juga, saya pikir, merupakan perilaku yang bermanfaat) . Terima kasih kepada Gilles karena menunjukkan ini.

Ini hanya menggunakan pemisah bidang internal untuk memisahkan bidang.

Ini cukup ... cerewet ...

...Oh man. Saya tahu tab itu murah, tapi saya pikir itu setidaknya pintar. Sekarang saya baru saja terlambat ke pesta ...

mikeserv
sumber
Ini berfungsi pada set tes saya seperti yang Anda inginkan, tetapi tidak sesuai dengan tantangan. Saya suka, karena saya mungkin akan belajar sesuatu yang baru. Saya kira saya harus membaca tentang IFS
benda
1
@ M.Herzkamp - jika berperilaku berbeda tergantung pada apakah itu diatur ke spasi atau tidak. Kebanyakan orang membencinya karena mereka tidak mengerti dua kualitas utamanya - bahwa itu hanya beroperasi pada ekspansi ( $expandbukan (ex pand)) dan hal ifsws baru saja disebutkan. Lihat di sini
mikeserv
Ini tidak mengganti nama file di dalam direktori yang namanya mengandung spasi. Perbaikan akan diganti -execdengan -execdir. Å Kelainan lain IFSyang tidak Anda sebutkan adalah spasi tambahan dihapus. Perhatikan bahwa karena orang lain telah memperhatikan Anda juga perlu -Topsi mv, karena ketika target mvpanggilan adalah direktori yang ada.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles - preferensi saya adalah menggunakan sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;dan gumpalan lain pada find -type dperintah untuk membuat pohon hardlink cermin dan kemudian beroperasi pada itu, tapi saya menebak kedua menulis kode-golf sama sekali untuk operasi bergerak. Poin bagus tentang ruang terkemuka / tertinggal, meskipun saya pikir itu juga merupakan perilaku yang saya inginkan.
mikeserv
@Gilles - tapi omong-omong, ini bukan permainan khusus - ini adalah perilaku yang dimaksudkan dan dikendalikan standar . Bagian Field-Splitting adalah di antara sedikit di spec shell yang tidak mengandung kata-kata yang tidak ditentukan atau implementasi-didefinisikan . Tidak ada jaminan tersebut dengan zsh's fungsi builtin zmv misalnya.
mikeserv
2

PHP, 147 145 byte, 2 1 spasi s -> 146

function    s(){foreach(glob("*")as$n){is_dir($n)&&chdir($n)&s()|chdir("..");if($n<$r=strtr($n," ",_)){while(file_exists($r))$r.=_;rename($n,$r);}}}

fungsi rekursif. Jalankan dengans(".");

Ulangi globhasil untuk jalur yang diberikan:

  • jika direktori, berulang
  • ganti spasi dengan garis bawah
  • jika string berbeda
    • saat nama file baru diambil, tambahkan garis bawah
    • ganti nama file / direktori
Titus
sumber
php akan mengganti nama file di server ... Sekarang saya bertanya-tanya bagaimana cara mengubah nama file klien setiap kali mereka mengunjungi situs Anda: D
M.Herzkamp
1

Ruby 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end
gam3
sumber
6
Selamat datang di Golf Code! Gagasan di sini dalam tantangan kode-golf ini adalah menggunakan jumlah karakter paling sedikit. Itu berarti bahwa Anda pasti dapat menyingkirkan baris kosong dan tab, dan membuat nama variabel karakter tunggal, tetapi orang mencari segala macam dari cara-cara kreatif untuk mengurangi jumlah karakter.
Bukan karena Charles
Saya mendapatkan kesalahan, bahwa Direktori tidak kosong:gam3.rb:5:in `rename': Directory not empty - ./Te stSub or ./Te_stSub (Errno::ENOTEMPTY) from gam3.rb:5 from /usr/lib/ruby/1.8/find.rb:39:in `find' from /usr/lib/ruby/1.8/find.rb:38:in `catch' from /usr/lib/ruby/1.8/find.rb:38:in `find' from gam3.rb:3
M.Herzkamp
1

Python, 187

165, ditambah 22 poin penalti untuk ruang.

from os import*
u='_';j=path.join
for t,d,f in walk('.',0):
 for z in f+d:
  n=z.replace(' ',u)
  if n!=z:
   while path.exists(j(t,n)):n+=u
   rename(j(t,z),j(t,n))

166, menggunakan trik Emanuele :

Hanya satu ruang dalam yang ini!

from    os  import*
u='_';j=path.join
for t,d,f   in  walk('.',0):
    for z   in  f+d:
        n=z.replace(' ',u)
        if  n!=z:
            while   path.exists(j(t,n)):n+=u
            rename(j(t,z),j(t,n))
Henry Keiter
sumber
Ini bekerja untuk saya. +1
M.Herzkamp
hapus spasi di awal baris dan gunakan tab - mereka bukan spasi jadi hitung hanya sekali
chill0r
@ chill0r Itulah versi kedua; semua spasi diganti dengan tab kecuali satu (kecuali SO masih menampilkannya sebagai spasi).
Henry Keiter
1

LiveScript - 166

(Ganti spasi dengan tab.)

(a=->(s=require \fs)readdirSync(it)map (f)->f=it+'/'+f;r=f.replace /\s/g,i='';(while f!=r&&s.existsSync r=>r+=i++);s.statSync(f)isDirectory(s.renameSync f,r)&&a r) \.

Berdasarkan nderscore ini versi yang dioptimalkan dari CPU1 's jawaban .

nyuszika7h
sumber
Bekerja! +1 Saya akan menghapus komentar saya sebelumnya untuk merapikan pos ini.
M.Herzkamp
0

Bash 4+ 111 byte

shopt -s dotglob globstar
for f in **
do
n=${f// /}
while [[ $f != $n && -e $n ]]
do n+=1
done
mv "$f" $n
done

sumber
1
Masalah yang sama dengan beberapa entri lainnya: Anda mengganti spasi di direktori induk dan mv tidak dapat menemukannya. Anda juga harus mengubah arah traversion, jika tidak Anda mengganti nama direktori dan mv tidak dapat menemukan file di dalamnya.
M.Herzkamp
0

Groovy, 139 karakter

def c
c={
f->
def g=new File(f.parent,f.name.replaceAll('\\s',''))
f.renameTo(g)
!g.directory ?: g.eachFile(c)
}
new File('.').eachFile(c)

menurut komentar @ edc65

Groovy, menangani tabrakan, 259 karakter

def c
c={
p,l,f->
def g=new File(p,f.name.replaceAll('\\s',''))
f==g?:
(g.exists()?f.renameTo(g.toString()+l.indexOf(f.name)):f.renameTo(g))
!g.directory?:g.eachFile(c.curry(g,g.list().toList()))
}
def r=new File('.')
r.eachFile(c.curry(r,r.list().toList()))
Gabung
sumber
1
Ini tidak menangani tabrakan.
edc65
Pastikan file diganti nama sebelum direktori induknya, dan bahwa spasi di direktori induk tidak diganti.
M.Herzkamp
Saya yakin tidak apa
masuk
0

POSIX (Diuji pada zsh) + perintah Linux dasar 151

export IFS='
'
for f in $(ls -R1);do export n=$(echo $f|tr ' ' '_');yes n|mv $f $n || yes n|mv $f `echo $n;echo $f|md5sum`
done
LinGeek
sumber
@ M.Herzkamp Diperbaiki.
LinGeek
Beberapa hal: apa fungsi ekspor IFS dan c di ls -cR? Dan versi mv apa yang Anda butuhkan untuk opsi --reply? (Saya memiliki 8.13, dan tidak mengenali opsi). Juga untuk mendapatkan skor yang lebih baik, Anda harus menyingkat nama variabel Anda.
M.Herzkamp
C menggantikan spasi dengan baris baru. IFS berhenti spasi menjadi pemisah. --Reply berasal dari versi lama dan akan diperbaiki.
LinGeek
1
Apakah Anda melewatkan mv kedua pada baris 5? Dan saya pikir satu gema di baris itu salah.
M.Herzkamp
1
$(ls -CR)benar-benar palsu. The -cpilihan adalah tidak berguna, dan -Rmembuat Anda file tanpa direktori mereka, yang sia-sia. Arsitektur Anda secara mendasar tidak akan menangani nama file yang mengandung baris baru. Anda perlu set -fatau nama file yang berisi wildcard akan meledak. exporttidak berguna. Samar-samar saya bisa melihat apa yang Anda coba lakukan untuk menyatukan file, tetapi perpipaannya salah.
Gilles 'SANGAT berhenti menjadi jahat'