Odio gli spazi nei nomi dei file


61

È semplice. Non sopporto quando le persone usano gli spazi quando nominano i file. Talvolta rovina i comandi della console e rende brutta l'output di ls.

La sfida è scrivere un programma (solo caratteri ASCII) che

  1. rinomina tutti i file (comprese le directory) nella directory corrente in versioni con spazi rimossi o sostituiti da '_'
  2. in caso di collisione, è necessario aggiungere un identificatore univoco (fino a te)
  3. scende ricorsivamente in tutte le sottodirectory

È possibile assumere nomi di percorsi in stile UNIX. Chi avrebbe comunque bisogno di questo programma su un computer Windows?

Questo è il golf del codice, vince il programma più breve (caratteri #ascii). Dal momento che odio gli spazi così tanto, ogni spazio deve essere contato due volte.

Fornisci la tua lingua, il punteggio, il programma e una breve descrizione di come eseguirlo.

Il programma deve essere compilato ed eseguito con ragionevole sforzo sulla mia macchina Linux.

EDIT: Poiché Etan ha richiesto una struttura di file per il test, ecco lo script che attualmente uso per creare un albero di file adatto:

#!/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

22
Ciò sta implorando una soluzione fatta senza caratteri ASCII.
Dennis Jaheruddin,

50
Ora voglio imparare Whitespace
BrunoJ,

10
@BrunoJ facendo questo in Whitespace richiederebbe prima di tutto di sviluppare un sistema di accesso ai file in WS. Penso che sarebbe più impegnativo della vera sfida.
Nzall,

7
Aspetto che qualcuno pubblichi una soluzione C / C ++ in modo da poterla rubare, compilare, pubblicare in esadecimale come codice macchina x86 con spazi ZERO! [o forse base64]
Mark K Cowan,

10
Odio i caratteri di sottolineatura nei nomi dei file. Usa trattini.
Dr. Rebmu,

Risposte:


10

Coreshils Zsh + GNU - 48 byte (1 spazio)

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

È strano che odi (ASCII) gli spazi ma che vada bene con le schede e le nuove righe, ma immagino che ci voglia tutto.

zmv risolve molti problemi di ridenominazione dei file in modo conciso (e solo leggermente oscuro). Tuttavia, insiste sul fatto che gli obiettivi siano unici; mentre puoi facilmente aggiungere suffissi univoci, aggiungere un suffisso solo se fosse necessario richiede di ripetere tutto il lavoro. Quindi invece eseguo il ciclo manualmente e faccio affidamento su GNU mv per aggiungere un identificatore univoco in caso di collisione ( --backupopzione, inoltre --no-target-directorynel caso in cui una destinazione sia una directory esistente, altrimenti mvsi sposterebbe il sorgente all'interno di quella directory).

(od)è un qualificatore glob per ordinare l'output con le directory che compaiono dopo il loro contenuto (come find -depth). Dinclude file dot nel glob. :he :tsono modificatori della storia simili a dirnamee basename.

mvsi lamenta che viene chiamato per rinominare i file a se stessi, perché il glob include nomi di file senza spazi. È la vita.

Versione non golfata:

for x in **/*\ *(Dod); do
  mv --no-target-directory --backup=numbered $x ${x:h}/${${x:t}// /}
done

1
questo non rinomina affatto i miei file!
M.Herzkamp,

@ M.Herzkamp Oh, giusto, le zmvbombe prima mvhanno la possibilità di risolvere le collisioni. Ok, lo sto facendo manualmente. Risulta essere esattamente della stessa lunghezza se salto i file punto e salva anche un personaggio se non lo faccio.
Gilles 'SO- smetti di essere malvagio' l'

1
Adesso funziona. A proposito: ho incluso la penalità di spazio in un momento in cui avevo davvero rancore verso gli spazi;) Ironia della sorte, non ho escluso gli spazi quando ho pubblicato la sfida: P
M.Herzkamp

13

Bash 116 byte, 16 spazi

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

Non ho eliminato gli errori per guadagnare un paio di byte in più. Questo non avrà alcuna collisione.

Se ci si findpuò aspettare GNU non posix , questo può essere ulteriormente abbreviato:

Bash 110 byte, 15 spazi

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

La rimozione di spazi anziché la loro sostituzione utilizza due byte in meno:

Bash 108 byte, 15 spazi

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

Nota: se è possibile utilizzare le schede anziché gli spazi, è necessario solo 1 spazio (quello nella regola di corrispondenza per la sostituzione alla riga 2).

Grazie a Dennis per aver trovato un bug sulla doppia virgoletta (e aver fornito la soluzione)


11
LO SPAZIO SUPPLEMENTARE È DIETRO trovare QUI PER RIPRISTINARLO ??? ;-)
M.Herzkamp

@ M.Herzkamp Pensavo fosse un errore di copia e incolla, ma in realtà è lì. Suppongo di aver guadagnato altri 2 punti. Inoltre, -depthin GNU può essere sostituito da -d, anche se si lamenta di essere deprecato. Non conosco le regole del golf, posso farlo?
pqnet,

2
Finché funziona, lo permetto. Se la deprecazione dovesse diventare rimozione in una versione futura, potrei dover tornare a questa risposta e ridimensionarla per non essere corretta ;-)
M.Herzkamp

2
Questo non funzionerà correttamente se uno dei nomi di file contiene una doppia citazione. Per risolvere questo problema, è possibile utilizzare bash -c 'B=${0##*/}...' {} \;invece, che in realtà è più breve.
Dennis,

3
Immagino che sarò quel ragazzo, che succede con la Nvariabile? Non è mai definito ...
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)

solo 2 spazi se usi la tab per il rientro :-)


Immagino che la maggior parte delle altre risposte potrebbe migliorare il loro punteggio utilizzando anche le schede anziché gli spazi.
Kasperd,

Ma la tua richiesta utilizza spazi, no? (+1 per il codice di lavoro)
M.Herzkamp

Non so come associare i caratteri di tabulazione nella risposta ...
Emanuele Paolini,

2
sostituito con schede :-)
Emanuele Paolini

3
Che brutto ... Beh, immagino di averlo chiesto :(
M.Herzkamp

5

Se l'ordine dei suffissi dei file scontrati non ha bisogno di dare un precedente al file preesistente, allora per me funziona quanto segue:

bash / find / mv 84 byte, 16 spazi

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

bash / find / mv 82 byte, 14 spazi

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

Coccole &&per risparmiare due byte di spazio.

bash / find / mv 60 byte, 11 spazi

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

Elimina la protezione dagli errori in modo da ottenere errori da MV su file che non hanno spazi per iniziare.

Modifica: sono state eliminate le citazioni {}come ricordato da Dennis. Ha anche permesso finddi urlare portabilità e deprecazione nella versione più corta in cui si mvsta già urlando di spostare un file su se stesso.

Modifica 2: aggiunto -Tal mvcomando per evitare di annidare le directory invece di rinominare come indicato da pqnet. Espansione delle parentesi graffe usata al costo di un personaggio oltre a utilizzare uno spazio.


Puoi usare al -dposto di -depthe non hai bisogno delle virgolette in giro {}.
Dennis,

@Dennis Sì. Ho visto la -dconversazione sulla risposta di pqnet ma ho pensato che, dato che stavo zittendo le mvurla, avrei evitato le findurla. Anche se probabilmente dovrei accorciarlo per quello urlante. E sì, cito sempre {}per qualche motivo anche se so che non è necessario in questo caso. Forza dell'abitudine immagino.
Etan Reisner,

1
Quando si verifica una collisione sui nomi delle directory, ne inseriranno uno nell'altro (e non gli spazi). Usa l' -Topzione per mvevitarlo
pqnet

Funziona e ho detto nella sfida che l'appendice dipende da te. +1
M.Herzkamp

4

NodoJS - 209 byte, 3 spazi bianchi

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('.');

Non ho familiarità con node.js. Come lo eseguirò?
M.Herzkamp,

Avrai bisogno dei nodejs eseguibili del nodo ; salvarlo in un file ed eseguirlonode file.js
cPu1

7
Ottengo un'eccezione TypeError: Object #<Object> has no method 'exists'. Indovina dove: è in linea 1! : D
M.Herzkamp,

L'ho provato. Comunque, ho sostituito esiste con la sua controparte sincrona. Puoi provare adesso?
cPu1,

1
Ho installato solo la versione 0.6.12. Questo potrebbe essere il problema.
M.Herzkamp,

2

Bash - 86 byte

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done

Oops, darò un'occhiata
Subbeh,

2
Inoltre, gli spazi vengono contati due volte ;-)
M.Herzkamp,

cosa intendi esattamente con gli spazi vengono contati due volte?
Subbeh,

1
Puoi salvare molti personaggi abbreviati --backupin--b

1
Sì, ora funziona anche con il mio set di test! +1
M.Herzkamp,

2

Bash + Perl rename64

( renameè lo script Perl su Debian e derivati, non il comando util-linux.)

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;

11
Cosa succede se "my file.txt" e "my_file.txt" sono entrambi presenti?
M.Herzkamp,

1
Oh vero ...
Ci stiamo

1
*dovrebbe essere {}, così com'è, rinomina solo i file il cui nome appare nella directory corrente. Questo non aggiunge un suffisso in caso di collisione. Potresti risparmiare un bel po 'omettendo -name "* *"poiché renameignora silenziosamente i file il cui nome non viene trasformato.
Gilles 'SO- smetti di essere malvagio'

2

POSIX sh+ GNU find+ GNU mv67 byte ASCII + uno spazio (letterale)

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

Non so se si adatta, ma con questo ogni sequenza di spazi viene elisa in un singolo _- mi piace comunque. In realtà qualsiasi sequenza tranne gli spazi iniziali / finali che sono - quelli vengono automaticamente troncati (che è anche, a mio avviso, un comportamento benefico) . Grazie a Gilles per averlo segnalato.

Questo utilizza solo il separatore di campo interno per separare i campi.

È abbastanza ... loquace ...

...Oddio. Sapevo che la cosa delle schede era economica, ma pensavo che fosse almeno intelligente. Ora sono solo in ritardo alla festa ...


Funziona sul mio set di test come previsto, ma non come richiede la sfida. Mi piace, però, perché probabilmente imparerò qualcosa di nuovo. Immagino che dovrò leggere su questa IFScosa magica ...
M.Herzkamp,

1
@ M.Herzkamp - ifs si comporta diversamente a seconda che sia impostato su spazi bianchi o meno. La maggior parte delle persone lo odiano perché non capiscono le sue due qualità primarie - che opera solo su espansioni ( $expandnon (ex pand)) e sulla cosa ifsws appena menzionata. Guarda qui
mikeserv l'

Questo non rinomina i file all'interno delle directory i cui nomi contengono spazi. Una soluzione sarebbe sostituire -execcon -execdir. Un'altra stranezza IFSche non stai menzionando è che gli spazi finali vengono eliminati. Si noti che come altri hanno notato è necessario anche l' -Topzione mv, per quando la destinazione di una mvchiamata è una directory esistente.
Gilles 'SO- smetti di essere malvagio' l'

@Gilles - la mia preferenza sarebbe quella di usare sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;e altri globs su un find -type dcomando per creare un albero speculare di hardlink e poi operare su quelli, ma sto indovinando a scrivere un code-golf per un'operazione di spostamento. Un buon punto sugli spazi iniziali / finali, anche se penso che sia anche un comportamento che preferirei.
Mikeserv,

@Gilles - ma a proposito, non è una stranezza - è un comportamento previsto e controllato dagli standard . La sezione di suddivisione del campo è tra le pochissime nelle specifiche della shell che non contiene le parole non specificate o definite dall'implementazione . Ad esempio, non esistono garanzie del genere con zshla funzione integrata zmv .
Mikeserv,

2

PHP, 147 145 byte, 2 1 spazio 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);}}}

funzione ricorsiva. Corri cons(".");

Scorrere i globrisultati per il percorso specificato:

  • se directory, ricorrere
  • sostituire gli spazi con trattino basso
  • se le stringhe differiscono
    • mentre viene preso il nuovo nome file, aggiungi il trattino basso
    • rinomina file / directory

php rinominerà i file sul server ... Ora mi chiedo come modificare i nomi dei file di un client ogni volta che visitano il tuo sito: D
M.Herzkamp

1

Ruby 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end

6
Benvenuti in Code Golf! L'idea qui in queste sfide di code-golf è quella di utilizzare il minor numero di caratteri. Ciò significa che puoi sicuramente eliminare le righe e le schede vuote e creare nomi di variabili a carattere singolo, ma le persone cercano tutti i tipi di modi creativi per ridurre il conteggio dei personaggi.
Non che Charles il

Viene visualizzato un errore che la Directory non è vuota: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, più 22 punti di penalità per gli spazi.

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, usando il trucco di Emanuele :

Solo un singolo spazio in questo!

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

Questo funziona per me. +1
M.Herzkamp

rimuovi gli spazi all'inizio delle linee e usa le schede - non sono spazi quindi conta solo una volta
chill0r

@ chill0r Ecco la seconda versione; tutti gli spazi vengono sostituiti con tab tranne uno (tranne SO li visualizza comunque come spazi).
Henry Keiter,

1

LiveScript - 166

(Sostituisci gli spazi con le schede.)

(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) \.

Sulla base di nderscore versione ottimizzata di CPU1 's risposta .


Lavori! +1 Ho intenzione di eliminare i miei commenti prima di riordinare questo post.
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

1
Stessi problemi di molte altre voci: sostituisci gli spazi nelle directory principali e mv non riesce a trovarli. Inoltre è necessario cambiare la direzione della traversione, altrimenti si rinominano le directory e mv non riesce a trovare i file all'interno.
M.Herzkamp,

0

Groovy, 139 caratteri

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)

secondo il commento di @ edc65

Groovy, gestire le collisioni, 259 caratteri

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

1
Questo non gestisce le collisioni.
edc65,

Assicurarsi che i file vengano rinominati prima delle rispettive directory principali e che gli spazi nelle directory principali non vengano sostituiti.
M.Herzkamp,

Sono sicuro che va bene
accedi il

0

POSIX (testato su zsh) + comandi Linux di base 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

@ M.Herzkamp Fixed.
LinGeek,

Diverse cose: qual è la funzione di esportazione IFS e la c in ls -cR? E quale versione di mv hai bisogno per l'opzione --reply? (Ho 8.13 e non riconosce l'opzione). Inoltre, per ottenere un punteggio migliore, è necessario abbreviare i nomi delle variabili.
M.Herzkamp,

La c sostituisce gli spazi con nuove righe. L'IFS impedisce che gli spazi siano separatori. --Reply è dalle vecchie versioni e sta per essere riparato.
LinGeek,

1
Ti manca un secondo mv in linea 5? E penso che un'eco in quella linea sia sbagliata.
M.Herzkamp,

1
$(ls -CR)è completamente falso. L' -copzione è inutile e -Rti dà i file senza la loro directory, il che è inutile. Fondamentalmente la tua architettura non gestirà i nomi dei file contenenti newline. È necessario set -faltrimenti esploderanno nomi di file contenenti caratteri jolly. exportè inutile. Posso vagamente vedere cosa stai cercando di fare per unificare i file, ma le tubazioni sono sbagliate.
Gilles 'SO- smetti di essere malvagio'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.