Ich hasse Leerzeichen in Dateinamen

61

Es ist einfach. Ich kann es nicht ausstehen, wenn Leute Leerzeichen beim Benennen von Dateien verwenden. Es zerstört manchmal Konsolenbefehle und macht die Ausgabe von ls hässlich.

Die Herausforderung besteht darin, ein Programm (nur ASCII-Zeichen) zu schreiben, das

  1. Benennt alle Dateien (einschließlich der Verzeichnisse) im aktuellen Verzeichnis in Versionen um, bei denen Leerzeichen entfernt oder durch "_" ersetzt wurden.
  2. Bei einer Kollision müssen Sie eine eindeutige Kennung anhängen (ganz nach Ihren Wünschen).
  3. rekursiv in alle Unterverzeichnisse absteigend

Sie können Pfadnamen im UNIX-Stil annehmen. Wer würde dieses Programm auf einem Windows-Computer benötigen?

Dies ist Codegolf, das kürzeste Programm gewinnt (#ascii Zeichen). Da ich Räume so sehr hasse, muss jeder Raum zweimal gezählt werden.

Bitte geben Sie Ihre Sprache, Partitur, Programm und eine kurze Beschreibung der Ausführung an.

Das Programm muss mit vertretbarem Aufwand auf meinem Linux-Rechner kompiliert und ausgeführt werden.

BEARBEITEN: Da Etan zum Testen eine Dateistruktur angefordert hat, verwende ich das folgende Skript, um einen geeigneten Dateibaum zu erstellen:

#!/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
quelle
22
Dies bittet um eine Lösung ohne ASCII-Zeichen.
Dennis Jaheruddin
50
Jetzt möchte ich Whitespace lernen
BrunoJ
10
@BrunoJ Wenn Sie dies in Whitespace tun, müssen Sie zuerst ein Dateizugriffssystem in WS entwickeln. Ich denke, das wäre herausfordernder als die eigentliche Herausforderung.
Nzall
7
Ich warte darauf, dass jemand eine C / C ++ - Lösung veröffentlicht, damit ich sie stehlen, kompilieren und hexadezimal als x86-Maschinencode mit NULL Leerzeichen veröffentlichen kann! [oder vielleicht base64]
Mark K Cowan
10
Ich hasse Unterstriche in Dateinamen. Verwenden Sie Bindestriche.
Dr. Rebmu

Antworten:

10

Zsh + GNU coreutils - 48 Bytes (1 Leerzeichen)

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

Es ist seltsam, dass Sie (ASCII-) Leerzeichen hassen, aber Tabulatoren und Zeilenumbrüche in Ordnung sind, aber ich denke, es sind alle Arten von Leerzeichen erforderlich.

zmv löst viele Probleme beim Umbenennen von Dateien in Kürze (und nur geringfügig undeutlich). Es besteht jedoch darauf, dass die Ziele eindeutig sind. Sie können zwar problemlos eindeutige Suffixe hinzufügen, das Hinzufügen eines Suffixes ist jedoch nur dann erforderlich, wenn die gesamte Arbeit wiederholt werden muss. Stattdessen schleife ich manuell und verlasse mich bei Kollisionen auf GNU mv , um einen eindeutigen Bezeichner anzufügen ( --backupOption, plus, --no-target-directorywenn ein Ziel ein vorhandenes Verzeichnis ist, da sonst mvdie Quelle in dieses Verzeichnis verschoben würde).

(od)ist ein Glob-Qualifikationsmerkmal zum Sortieren der Ausgabe nach Verzeichnissen, die nach ihrem Inhalt angezeigt werden (wie bei Suchen -depth). DEnthält Punktdateien im Glob. :hund :tsind History-Modifikatoren ähnlich wie dirnameund basename.

mvbeschwert sich, dass es aufgerufen wird, um Dateien in sich selbst umzubenennen, da der Glob Dateinamen ohne Leerzeichen enthält. So ist das Leben.

Ungolfed-Version:

for x in **/*\ *(Dod); do
  mv --no-target-directory --backup=numbered $x ${x:h}/${${x:t}// /}
done
Gilles 'SO - hör auf böse zu sein'
quelle
1
das benennt meine Dateien überhaupt nicht um!
M.Herzkamp
@M.Herzkamp Oh, richtig, zmvBomben raus haben vorher mvdie Chance Kollisionen auszusortieren. Ok, ich mache das manuell. Es stellt sich heraus, dass die Länge genau gleich ist, wenn ich Punktedateien überspringe und sogar ein Zeichen speichere, wenn ich dies nicht tue.
Gilles 'SO- hör auf böse zu sein'
1
Jetzt geht es. Btw: Ich habe die Raumstrafe zu einer Zeit
eingefügt,
13

Bash 116 Bytes, 16 Leerzeichen

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

Ich habe Fehler nicht unterdrückt, um ein paar Bytes mehr zu gewinnen. Dies wird keine Kollisionen haben.

Wenn nicht posix GNU finderwartet werden kann, kann dies weiter verkürzt werden:

Bash 110 Bytes, 15 Leerzeichen

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

Das Entfernen statt Ersetzen von Leerzeichen benötigt zwei Bytes weniger:

Bash 108 Bytes, 15 Leerzeichen

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

Hinweis: Wenn Tabulatoren anstelle von Leerzeichen verwendet werden können, wird nur 1 Leerzeichen benötigt (das in der Übereinstimmungsregel für die Ersetzung in Zeile 2).

Vielen Dank an Dennis für das Auffinden des Fehlers in Anführungszeichen (und die Bereitstellung der Lösung)

pqnet
quelle
11
IST DER ZUSÄTZLICHE RAUM HINTER FINDEN, UM MICH ZU VERSPotten ??? ;-)
M.Herzkamp
@ M.Herzkamp Ich dachte es war ein Copy & Paste Fehler, aber es ist eigentlich da. Ich schätze, ich habe 2 weitere Punkte gewonnen. Auch -depthin GNU kann durch ersetzt werden -d, obwohl es sich beschwert, dass es veraltet ist. Ich kenne die Regeln des Golfsports nicht, kann ich das tun?
pqnet
2
Solange es funktioniert, erlaube ich es. Sollte die Abschreibung in einer zukünftigen Version jedoch aufgehoben werden, muss ich möglicherweise auf diese Antwort zurückkommen und sie ablehnen, weil sie nicht korrekt ist ;-)
M.Herzkamp,
2
Dies funktioniert nicht richtig, wenn einer der Dateinamen ein doppeltes Anführungszeichen enthält. Um dies zu beheben, können Sie bash -c 'B=${0##*/}...' {} \;stattdessen verwenden, was tatsächlich kürzer ist.
Dennis
3
Ich denke, ich werde der Typ sein, was ist mit NVariable los? Es ist nie definiert ...
Steven Penny
9

Python 180 Bytes

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)

nur 2 Leerzeichen, wenn Sie die Tabulatortaste zum Einrücken verwenden :-)

Emanuele Paolini
quelle
Ich denke, die meisten anderen Antworten könnten ihre Punktzahl verbessern, indem sie Tabulatoren anstelle von Leerzeichen verwenden.
Kasperd
Aber Ihre Eingabe verwendet Leerzeichen, nicht wahr? (+1 für
Arbeitscode
Ich weiß nicht, wie man Tabulatorzeichen in der Antwort angibt ...
Emanuele Paolini
2
durch Tabs ersetzt :-)
Emanuele Paolini
3
Wie hässlich ... Nun, ich glaube, ich habe danach gefragt :(
M.Herzkamp
5

Wenn die Reihenfolge der kollidierten Dateisuffixe der bereits vorhandenen Datei keinen Präzedenzfall geben muss, funktioniert für mich Folgendes:

bash / find / mv 84 Bytes, 16 Leerzeichen

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

bash / find / mv 82 Bytes, 14 Leerzeichen

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

Gekuschelt &&, um zwei Platzbytes zu sparen.

bash / find / mv 60 Bytes, 11 Leerzeichen

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

Lässt den Fehlerschutz fallen, sodass er Fehler von mv für Dateien erhält, die anfangs keine Leerzeichen enthalten.

Bearbeiten: Löschte die Anführungszeichen aus, {}wie von Dennis erinnert. Auch darf findüber Portabilität und Verfall in der kürzesten Version geschrien werden, wo mvschon über das Verschieben einer Datei auf sich geschrien wird.

Bearbeiten 2: -TZum mvBefehl hinzugefügt , um das Verschachteln von Verzeichnissen zu vermeiden, anstatt sie umzubenennen, wie von pqnet hervorgehoben. Verwendete Klammererweiterung zum Preis von einem Zeichen über nur ein Leerzeichen.

Etan Reisner
quelle
Sie können -danstelle von verwenden, -depthund Sie brauchen keine Anführungszeichen {}.
Dennis
@ Tennis Ja. Ich sah die -dUnterhaltung auf pqnets Antwort, dachte aber, da ich das mvSchreien zum Schweigen brachte, würde ich das findSchreien vermeiden . Obwohl ich es wahrscheinlich für den Schreienden kürzen sollte. Und ja, ich zitiere immer {}aus irgendeinem Grund, obwohl ich weiß, dass Sie das in diesem Fall nicht müssen. Ich schätze, die Macht der Gewohnheit.
Etan Reisner
1
Wenn bei Verzeichnisnamen Kollisionen auftreten, werden diese ineinander verschoben (und keine Leerzeichen entfernt). Verwenden Sie die -TOption, mvum dies zu vermeiden
pqnet
Dies funktioniert, und ich sagte in der Herausforderung, dass der Anhang bis zu Ihnen ist. +1
M.Herzkamp
4

NodeJS - 209 Bytes, 3 Whitespaces

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
quelle
Ich kenne mich mit node.js nicht aus. Wie würde ich es laufen lassen?
M.Herzkamp
Sie benötigen die ausführbaren Node- Nodes . Speichere es in einer Datei und node file.js
starte
7
Ich habe eine Ausnahme TypeError: Object #<Object> has no method 'exists'. Ratet mal wo: es steht in Zeile 1! : D
M.Herzkamp
Ich habe es getestet Wie auch immer, ich habe es durch sein synchrones Gegenstück ersetzt. Kannst du es jetzt versuchen?
cPu1
1
Ich habe nur die Version 0.6.12 installiert. Das könnte das Problem sein.
M.Herzkamp
2

Bash - 86 Bytes

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done
Subbeh
quelle
Hoppla,
wirf
2
Außerdem werden Leerzeichen zweimal gezählt
;-)
Was genau meinst du mit Leerzeichen, die doppelt gezählt werden?
Subbeh
1
Sie können viele Zeichen speichern, indem Sie --backupmit--b
1
Ja, jetzt funktioniert es auch mit meinem Test-Set! +1
M.Herzkamp
2

Bash + Perl rename64

( renameIst das Perl-Skript auf Debian und Derivaten, nicht der Befehl util-linux.)

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;
german_guy
quelle
11
Was passiert, wenn sowohl "my file.txt" als auch "my_file.txt" vorhanden sind?
M.Herzkamp
1
Oh wahr ..
Ich arbeite
1
*sollte sein {}, wie es steht, benennt dies nur Dateien um, deren Name im aktuellen Verzeichnis erscheint. Bei einer Kollision wird kein Suffix angehängt. Sie könnten einiges sparen, indem Sie weglassen, -name "* *"da renameDateien, deren Name nicht transformiert wird, unbemerkt ignoriert werden.
Gilles 'SO- hör auf böse zu sein'
2

POSIX sh+ GNU find+ GNU mv67 ASCII-Bytes + ein (Literal-) Leerzeichen

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

Ich weiß nicht, ob es passt, aber damit wird jede Abfolge von Leerzeichen zu einem einzigen _- ich mag es trotzdem. Eigentlich jede Sequenz, außer führende / nachfolgende Leerzeichen - diese werden automatisch abgeschnitten (was auch, wie ich finde, ein nützliches Verhalten ist) . Vielen Dank an Gilles für diesen Hinweis.

Hierbei wird nur das interne Feldtrennzeichen zum Trennen von Feldern verwendet.

Es ist ziemlich ... gesprächig ...

...Oh Mann. Ich wusste, dass das Tab-Ding billig war, aber ich fand es zumindest klug. Jetzt bin ich nur zu spät zur Party ...

mikeserv
quelle
Dies funktioniert auf meinem Test-Set so, wie Sie es beabsichtigt haben, aber nicht so, wie es die Herausforderung erfordert. Ich mag es aber, weil ich wahrscheinlich etwas Neues lernen werde. Ich denke, ich muss über dieses IFSmagische Ding nachlesen ...
M.Herzkamp
1
@ M.Herzkamp - ifs verhält sich unterschiedlich, je nachdem, ob Whitespace eingestellt ist oder nicht. Die meisten Leute hassen es, weil sie die beiden Hauptmerkmale nicht verstehen - dass es nur bei Erweiterungen ( $expandnicht (ex-pand)) und der eben erwähnten ifsws-Sache funktioniert. Schauen Sie hier
mikeserv
Hiermit werden keine Dateien in Verzeichnissen umbenannt, deren Namen Leerzeichen enthalten. Ein Update wäre zu ersetzen -execmit -execdir. Eine weitere Besonderheit IFS, die Sie nicht erwähnen, ist, dass nachgestellte Leerzeichen gelöscht werden. Beachten Sie, dass Sie, wie andere bemerkt haben, auch diese -TOption benötigen mv, wenn das Ziel eines mvAnrufs ein vorhandenes Verzeichnis ist.
Gilles 'SO- hör auf böse zu sein'
@Gilles - Ich würde es sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;vorziehen , mit und anderen Globs in einem find -type dBefehl einen gespiegelten Baum von Hardlinks zu erstellen und diese dann zu bearbeiten , aber ich rate nach wie vor, überhaupt einen Code-Golf für eine Bewegungsoperation zu schreiben. Guter Punkt zu den führenden / nachfolgenden Leerzeichen, obwohl ich denke, dass dies auch ein Verhalten ist, das ich bevorzugen würde.
mikeserv
@Gilles - aber im Übrigen handelt es sich nicht um eine Eigenheit - es handelt sich um ein beabsichtigtes und standardkontrolliertes Verhalten. Der Field-Splitting- Abschnitt gehört zu den wenigen in der Shell-Spezifikation, in denen die Wörter nicht angegeben oder durch die Implementierung definiert sind . Es gibt zum Beispiel keine solchen Garantien für zshdie eingebaute Funktion zmv .
mikeserv
2

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

rekursive Funktion. Laufen Sie mits(".");

globErgebnisse für den angegebenen Pfad durchlaufen :

  • Wenn Verzeichnis, rekursiv
  • Ersetzen Sie Leerzeichen durch Unterstriche
  • Wenn die Zeichenfolgen unterschiedlich sind
    • Fügen Sie einen Unterstrich hinzu, während der neue Dateiname verwendet wird
    • Datei / Verzeichnis umbenennen
Titus
quelle
PHP wird die Dateien auf dem Server umbenennen ... Jetzt frage ich mich, wie ich die Dateinamen eines Clients ändern kann, wenn er Ihre Site besucht: D
M.Herzkamp
1

Rubin 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end
gam3
quelle
6
Willkommen bei Code Golf! Die Idee hier in diesen Code-Golf- Herausforderungen ist es, die geringste Anzahl von Zeichen zu verwenden. Das bedeutet , dass Sie auf jeden Fall loswerden Leerzeilen erhalten und Registerkarten und Variablennamen mit einem Zeichen, aber Menschen suchen nach allen möglichen von kreativen Möglichkeiten Zeichenanzahl zu reduzieren.
Nicht dass Charles
Ich erhalte eine Fehlermeldung, dass das Verzeichnis nicht leer ist: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, plus 22 Strafpunkte für die Räume.

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, mit Emanueles \ t Trick:

Nur ein einziger Platz in diesem!

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
quelle
Das funktioniert bei mir. +1
M.Herzkamp
entferne die Leerzeichen am Anfang der Zeilen und benutze Tabulatoren - es sind keine Leerzeichen, also zähle nur einmal
chill0r
@ chill0r Das ist die zweite Version. Alle Leerzeichen bis auf eines werden durch Tabulatoren ersetzt (mit Ausnahme von SO werden sie weiterhin als Leerzeichen angezeigt).
Henry Keiter
1

LiveScript - 166

(Ersetzen Sie Leerzeichen durch Tabulatoren.)

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

Basierend auf nderscore die optimierte Version der CPU1 ‚s Antwort .

nyuszika7h
quelle
Funktioniert! +1 Ich werde meine Kommentare früher löschen, um diesen Beitrag zu ordnen.
M.Herzkamp
0

Bash 4+ 111 Bytes

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

quelle
1
Gleiche Probleme wie bei mehreren anderen Einträgen: Sie ersetzen Leerzeichen in übergeordneten Verzeichnissen und mv kann sie nicht finden. Außerdem müssen Sie die Bewegungsrichtung ändern, andernfalls benennen Sie die Verzeichnisse um und mv kann die darin enthaltenen Dateien nicht finden.
M.Herzkamp
0

Groovy, 139 Zeichen

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)

laut @ edc65 Kommentar

Groovig, mit Kollisionen umgehen, 259 Zeichen

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()))
Anmeldung
quelle
1
Dies behandelt keine Kollisionen.
edc65
Stellen Sie sicher, dass die Dateien vor den übergeordneten Verzeichnissen umbenannt werden und dass die Leerzeichen in den übergeordneten Verzeichnissen nicht ersetzt werden.
M.Herzkamp
Ich bin mir sicher, dass es in Ordnung ist
melden Sie sich
0

POSIX (Getestet auf zsh) + grundlegende Linux-Befehle 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
quelle
@ M.herzkamp behoben.
LinGeek
Mehrere Dinge: Was ist die Funktion des Exports von IFS und des c in ls -cR? Und welche Version von mv benötigen Sie für die Antwortoption? (Ich habe 8.13, und es erkennt die Option nicht). Um eine bessere Punktzahl zu erzielen, sollten Sie auch Ihre Variablennamen abkürzen.
M.Herzkamp
Das c ersetzt Leerzeichen durch Zeilenumbrüche. Das IFS stoppt Leerzeichen als Trennzeichen. Die Antwort stammt aus alten Versionen und wird bald repariert.
LinGeek
1
Vermissen Sie eine zweite MV in Zeile 5? Und ich denke, ein Echo in dieser Zeile ist falsch.
M.Herzkamp
1
$(ls -CR)ist völlig falsch. Die -cOption ist nutzlos und -Rruft Dateien ohne ihr Verzeichnis ab, was sinnlos ist. Ihre Architektur kann grundsätzlich keine Dateinamen verarbeiten, die Zeilenumbrüche enthalten. Sie benötigen set -foder Dateinamen mit Platzhaltern explodieren. exportist nutzlos. Ich kann vage sehen, was Sie tun, um Dateien zu vereinheitlichen, aber die Weiterleitung ist falsch.
Gilles 'SO- hör auf böse zu sein'