NIENAWIDZĘ spacji w nazwach plików

61

To jest proste. Nie mogę znieść, gdy ludzie używają spacji podczas nazywania plików. Czasami psuje polecenia konsoli i powoduje, że wyjście ls jest brzydkie.

Wyzwanie polega na napisaniu programu (tylko znaki ascii), który

  1. zmienia nazwy wszystkich plików (w tym katalogów) w bieżącym katalogu na wersje ze spacjami usuniętymi lub zastąpionymi przez „_”
  2. w przypadku kolizji musisz dołączyć unikalny identyfikator (do ciebie)
  3. schodzi rekurencyjnie do wszystkich podkatalogów

Możesz założyć nazwy ścieżek w stylu UNIX. Kto i tak potrzebowałby tego programu na komputerze z systemem Windows?

To jest kod golfowy, wygrywa najkrótszy program (znaki #ascii). Ponieważ tak bardzo nienawidzę spacji, każde pole musi zostać policzone dwukrotnie.

Podaj swój język, wynik, program i krótki opis tego, jak go uruchomić.

Program musi się skompilować i uruchomić z rozsądnym wysiłkiem na moim komputerze z systemem Linux.

EDYCJA: Ponieważ Etan poprosił o strukturę plików do testowania, oto skrypt, którego obecnie używam do utworzenia odpowiedniego drzewa plików:

#!/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
źródło
22
To błaga o rozwiązanie wykonane bez znaków ascii.
Dennis Jaheruddin
50
Teraz chcę się nauczyć Białej Przestrzeni
BrunoJ
10
@BrunoJ robi to w Whitespace najpierw wymaga opracowania systemu dostępu do plików w WS. Myślę, że byłoby to trudniejsze niż rzeczywiste wyzwanie.
Nzall,
7
Oczekiwanie, aż ktoś opublikuje rozwiązanie C / C ++, abym mógł go ukraść, skompilować, opublikować szesnastkowo jako kod maszynowy x86 ze spacjami ZERO! [a może base64]
Mark K Cowan
10
Nienawidzę podkreśleń w nazwach plików. Użyj myślników.
Dr. Rebmu

Odpowiedzi:

10

Coreshils Zsh + GNU - 48 bajtów (1 spacja)

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

To dziwne, że nienawidzisz spacji (ASCII), ale nie masz nic przeciwko tabulatorom i nowym wierszom, ale myślę, że zajmuje to wszystko.

zmv rozwiązuje wiele problemów związanych ze zmianą nazw plików zwięźle (i tylko nieco niejasno). Podkreśla jednak, że cele są wyjątkowe; chociaż możesz łatwo dodawać unikalne sufiksy, dodawanie sufiksu tylko wtedy, gdy byłoby to potrzebne, wymaga ponownego wykonania całej pracy. Zamiast tego zapętlam ręcznie i polegam na GNU mv w celu dołączenia unikalnego identyfikatora w przypadku kolizji ( --backupopcja, plus --no-target-directoryw przypadku, gdy celem jest istniejący katalog, ponieważ w przeciwnym mvrazie przeniesie źródło do tego katalogu).

(od)jest globalnym kwalifikatorem do sortowania danych wyjściowych z katalogami pojawiającymi się po ich zawartości (np. find's -depth). Dzawiera pliki kropek w glob. :hi :tmodyfikatorami historii podobnymi do dirnamei basename.

mvnarzeka, że ​​wezwanie do zmiany nazw plików na siebie, ponieważ glob zawiera nazwy plików bez spacji. C'est la vie.

Wersja bez golfa:

for x in **/*\ *(Dod); do
  mv --no-target-directory --backup=numbered $x ${x:h}/${${x:t}// /}
done
Gilles
źródło
1
to wcale nie zmienia nazwy moich plików!
M.Herzkamp
@ M.Herzkamp Och, racja, zmvbomby już mvmają szansę rozwiązać kolizje. Ok, robię to ręcznie. Okazuje się, że ma dokładnie taką samą długość, jeśli pominę pliki kropkowe, a nawet zapisze znak, jeśli tego nie zrobię.
Gilles
1
Teraz działa. Btw: Uwzględniłem karę za miejsce w czasie, gdy naprawdę miałem pretensje do spacji;) Jak na ironię, nie wykluczałem spacji, gdy opublikowałem wyzwanie: P
M.Herzkamp
13

Bash 116 bajtów, 16 spacji

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

Nie pomijałem błędów, aby uzyskać jeszcze kilka bajtów. To nie spowoduje żadnych kolizji.

Jeśli findmożna oczekiwać GNU niep Posix , można to jeszcze bardziej skrócić:

Bash 110 bajtów, 15 spacji

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

Usuwanie spacji zamiast ich zastępowania powoduje użycie dwóch mniej bajtów:

Bash 108 bajtów, 15 spacji

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

Uwaga: jeśli zamiast spacji można użyć tabulatorów, potrzebna jest tylko 1 spacja (ta w regule dopasowania do zamiany w linii 2).

Podziękowania dla Dennisa za znalezienie błędu przy podwójnej wycenie (i zapewnienie rozwiązania)

pqnet
źródło
11
CZY DODATKOWA PRZESTRZEŃ ZA POMOCĄ MIEJSCA? ;-)
M.Herzkamp
@ M.Herzkamp Myślałem, że to błąd kopiowania i wklejania, ale tak naprawdę tam jest. Chyba zdobyłem jeszcze 2 punkty. Również -depthw GNU można zastąpić -d, choć narzeka, że ​​jest przestarzałe. Nie wiem o zasadach gry w golfa, mogę to zrobić?
pqnet,
2
Dopóki to działa, pozwalam na to. Gdyby jednak wycofanie stało się usunięciem w przyszłej wersji, być może będę musiał wrócić do tej odpowiedzi i głosować za jej niepoprawność ;-)
M.Herzkamp
2
To nie będzie działać poprawnie, jeśli którykolwiek z nazw plików zawiera podwójny cudzysłów. Aby to naprawić, możesz użyć bash -c 'B=${0##*/}...' {} \;zamiast tego, który w rzeczywistości jest krótszy.
Dennis,
3
Chyba będę tym facetem, co jest ze Nzmienną? Nigdy nie jest zdefiniowane ...
Steven Penny,
9

Python 180 bajtów

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)

tylko 2 spacje, jeśli używasz tabulacji do wcięcia :-)

Emanuele Paolini
źródło
Wydaje mi się, że większość innych odpowiedzi może poprawić ich wynik poprzez użycie tabulatorów zamiast spacji.
kasperd
Ale twoje zgłoszenie używa spacji, prawda? (+1 za działający kod)
M.Herzkamp
Nie wiem, jak dołączyć znaki tabulacji w odpowiedzi ...
Emanuele Paolini,
2
zastąpione zakładkami :-)
Emanuele Paolini
3
Jak brzydka ... Cóż, chyba poprosiłam o to :(
M.Herzkamp
5

Jeśli kolejność przyrostków plików kolidujących nie musi dawać precedensu wcześniej istniejącemu plikowi, wówczas dla mnie działa:

bash / find / mv 84 bajtów, 16 spacji

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

bash / find / mv 82 bajty, 14 spacji

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

Przytulony, &&aby zaoszczędzić dwa bajty miejsca.

bash / find / mv 60 bajtów, 11 spacji

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

Wyłącza ochronę przed błędami, aby uzyskać błędy z mv w plikach, które nie mają spacji na początek.

Edycja: Usunięto cytaty z {}przypomnienia Dennisa. Pozwolono także findkrzyczeć na temat przenośności i wycofania w najkrótszej wersji, w której mvjuż krzyczy o przeniesieniu pliku na siebie.

Edycja 2: Dodano -Tdo mvpolecenia, aby uniknąć zagnieżdżania katalogów zamiast zmiany nazwy, jak wskazał pqnet. Użyto rozszerzenia nawiasów klamrowych kosztem jednej postaci, a nie tylko jednego miejsca.

Etan Reisner
źródło
Możesz użyć -dzamiast -depthi nie potrzebujesz cytatów {}.
Dennis
@Dennis Tak. Widziałem -drozmowę na odpowiedź pqnet, ale zorientowali odkąd został uciszając mvkrzyki bym uniknąć findkrzyczeć. Chociaż prawdopodobnie powinienem to skrócić do krzyku. I tak, zawsze cytuję {}z jakiegoś powodu, chociaż wiem , że nie musisz w tym przypadku. Siła przyzwyczajenia.
Etan Reisner,
1
Kiedy nastąpi kolizja z nazwami katalogów, będą one umieszczane jeden w drugim (a nie usuwają spacji). Użyj -Topcji, aby mvtego uniknąć
pqnet
To działa i powiedziałem w wyzwaniu, że dodatek zależy od ciebie. +1
M.Herzkamp
4

NodeJS - 209 bajtów, 3 białe znaki

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
źródło
Nie jestem zaznajomiony z node.js. Jak miałbym to uruchomić?
M.Herzkamp
Będziesz potrzebował wykonywalnych nodejs ; zapisz go w pliku i uruchomnode file.js
cPu1
7
Dostaję wyjątek TypeError: Object #<Object> has no method 'exists'. Zgadnij gdzie: jest w linii 1! : D
M.Herzkamp
Przetestowałem to. W każdym razie zastąpiłem istnieje jego synchronicznym odpowiednikiem. Możesz teraz spróbować
cPu1
1
Mam zainstalowaną tylko wersję 0.6.12. To może być problem.
M.Herzkamp
2

Bash - 86 bajtów

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done
Subbeh
źródło
Ups, spójrz
Subbeh
2
Również spacje są liczone dwukrotnie ;-)
M.Herzkamp
co dokładnie masz na myśli mówiąc, że spacje są liczone dwukrotnie?
Subbeh
1
Możesz zapisać wiele znaków, skracając --backupdo--b
1
Tak, teraz działa również z moim zestawem testowym! +1
M.Herzkamp
2

Bash + Perl rename64

( renamejest skryptem Perla dla Debiana i pochodnych, a nie komendą util-linux).

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;
german_guy
źródło
11
Co się stanie, jeśli zarówno „mój plik.txt”, jak i „mój_plik.txt” są obecne?
M.Herzkamp
1
Och, prawda .. Pracuję nad tym wkrótce
german_guy
1
*powinno być {}, ponieważ w takim stanie zmienia się tylko pliki, których nazwa pojawia się w bieżącym katalogu. Nie dołącza to przyrostka w przypadku kolizji. Możesz sporo zaoszczędzić, pomijając, -name "* *"ponieważ renamedyskretnie ignoruje pliki, których nazwa nie została przekształcona.
Gilles
2

POSIX sh+ GNU find+ GNU mv67 bajtów ASCII + jedna (dosłowna) spacja

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

Nie wiem, czy to pasuje, ale z tym każda sekwencja spacji jest łączona w jedną _- i tak mi się podoba. Właściwie każda sekwencja, ale spacje wiodące / końcowe, które są - są one automatycznie obcinane (co, jak sądzę, jest również korzystnym zachowaniem) . Dzięki Gillesowi za zwrócenie na to uwagi.

To po prostu używa wewnętrznego separatora pól do oddzielenia pól.

Jest dość ... gadatliwy ...

...o stary. Wiedziałem, że zakładka jest tania, ale myślałem, że przynajmniej sprytna. Teraz jestem spóźniony na imprezę ...

mikeserv
źródło
Działa to na moim zestawie testowym, tak jak zamierzałeś, ale nie tak, jak wymaga tego wyzwanie. Lubię to, bo prawdopodobnie nauczę się czegoś nowego. Chyba będę musiał przeczytać o tej IFSmagicznej rzeczy ...
M.Herzkamp
1
@ M.Herzkamp - jeśli zachowuje się inaczej w zależności od tego, czy jest ustawiony na spację, czy nie. Większość ludzi go nienawidzi, ponieważ nie rozumie jego dwóch podstawowych cech - że działa tylko na rozszerzeniach ( $expandnie (ex pand)) i na wspomnianej właśnie kwestii ifsws. Spójrz tutaj
mikeserv
Nie zmienia to nazw plików w katalogach, których nazwy zawierają spacje. Fix byłoby wymienić -execz -execdir. Kolejnym dziwactwem, o IFSktórym nie wspominasz, jest to, że końcowe spacje są usuwane. Zwróć uwagę, że jak zauważyli inni, potrzebujesz również takiej -Topcji mv, gdy celem mvpołączenia jest istniejący katalog.
Gilles
@Gilles - wolę używać sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;i innych globów na find -type dkomendzie, aby utworzyć lustrzane drzewo linków twardych, a następnie operować na nich, ale domyślam się, że w ogóle piszę golfa kodowego dla operacji przenoszenia. Dobra uwaga na temat wiodących / końcowych spacji, choć myślę, że jest to również zachowanie, które wolałbym.
mikeserv
@Gilles - ale nawiasem mówiąc, nie jest to dziwactwo - jest to zamierzone i kontrolowane zachowanie. Sekcja Dzielenie pól jest jedną z niewielu w specyfikacji powłoki, która nie zawiera słów nieokreślonych lub zdefiniowanych w implementacji . Brak takich gwarancji z zsh„s funkcji wbudowanego zmv na przykład.
mikeserv
2

PHP, 147 145 bajtów, 2 1 spacje 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);}}}

funkcja rekurencyjna. Biegnij zs(".");

Pętla przez globwyniki dla danej ścieżki:

  • jeśli katalog, powtórz
  • zastąp spacje znakiem podkreślenia
  • jeśli łańcuchy się różnią
    • podczas pobierania nowej nazwy pliku dołącz podkreślnik
    • zmień nazwę pliku / katalogu
Tytus
źródło
php zmieni nazwę plików na serwerze ... Teraz zastanawiam się, jak zmienić nazwy plików klienta za każdym razem, gdy odwiedzają twoją stronę: D
M.Herzkamp
1

Ruby 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end
gam3
źródło
6
Witamy w Code Golf! Ideą w tych wyzwaniach golfa jest użycie jak najmniejszej liczby znaków. Oznacza to, że na pewno można pozbyć się pustych wierszy i zakładkach i dokonać nazw zmiennych pojedynczej postaci, ale ludzie patrzą na wszelkiego rodzaju z twórczych sposobów, aby zmniejszyć liczbę znaków.
Nie to, że Charles
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
Pojawia
1

Python, 187

165, plus 22 punkty karne za pola.

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, używając sztuczki Emanuele'a :

Tylko jedna przestrzeń w tym!

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
źródło
To działa dla mnie. +1
M.Herzkamp
usuń spacje na początku wiersza i użyj tabulatorów - nie ma spacji, więc policz tylko raz
chill0r 13.08.2014
@ chill0r To właśnie jest druga wersja; wszystkie spacje są zastąpione tabulatorami oprócz jednego (z wyjątkiem SO nadal wyświetla je jako spacje).
Henry Keiter,
1

LiveScript - 166

(Zastąp spacje tabulatorami.)

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

Na podstawie nderscore za zoptymalizowanej wersji z CPU1 „s odpowiedź .

nyuszika7h
źródło
Pracuje! +1 Mam zamiar usunąć moje komentarze wcześniej, aby uporządkować ten post.
M.Herzkamp
0

Bash 4+ 111 bajtów

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

źródło
1
Te same problemy, co kilka innych pozycji: Zastępujesz spacje w katalogach nadrzędnych i mv nie może ich znaleźć. Musisz także zmienić kierunek podróży, inaczej zmienisz nazwy katalogów i mv nie będzie w stanie znaleźć plików w środku.
M.Herzkamp
0

Groovy, 139 znaków

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)

zgodnie z komentarzem @ edc65

Groovy, radzić sobie z kolizjami, 259 znaków

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()))
Zaloguj sie
źródło
1
To nie obsługuje kolizji.
edc65,
Upewnij się, że nazwy plików są zmieniane przed ich katalogami nadrzędnymi, a spacje w katalogach nadrzędnych nie są zastępowane.
M.Herzkamp
Jestem pewien, że jest w porządku
zaloguj się
0

POSIX (testowany na zsh) + podstawowe komendy Linuksa 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
źródło
@ M.Herzkamp Naprawiono.
LinGeek,
Kilka rzeczy: jaka jest funkcja eksportu IFS i cw ls -cR? A do jakiej wersji mv potrzebujesz opcji --reply? (Mam 8.13 i nie rozpoznaje opcji). Aby uzyskać lepszy wynik, należy skracać nazwy zmiennych.
M.Herzkamp
Litera c zastępuje spacje znakiem nowej linii. IFS zatrzymuje spacje jako separatory. Opcja --reply pochodzi ze starych wersji i wkrótce zostanie naprawiona.
LinGeek
1
Czy brakuje ci drugiego mv w linii 5? I myślę, że jedno echo w tej linii jest błędne.
M.Herzkamp
1
$(ls -CR)jest całkowicie fałszywy. Ta -copcja jest bezużyteczna i -Rpozwala uzyskać pliki bez ich katalogu, co jest bezcelowe. Twoja architektura zasadniczo nie obsługuje nazw plików zawierających znaki nowej linii. Potrzebujesz, set -fbo inaczej nazwy plików zawierające symbole wieloznaczne wybuchną. exportjest bezużyteczny. Mogę niejasno zobaczyć, co próbujesz zrobić, aby ujednolicić pliki, ale orurowanie jest nieprawidłowe.
Gilles