Я ненавиджу пробіли у назвах файлів


61

Це просто. Я не витримую, коли люди використовують пробіли під час іменування файлів. Іноді він руйнує команди консолі і робить висновок ls потворним.

Завдання полягає в тому, щоб написати програму (лише символи ascii), яка

  1. перейменовує всі файли (включаючи каталоги) у поточному каталозі у версії з пробілами, вилученими або заміненими на "_"
  2. при зіткненні вам потрібно додати унікальний ідентифікатор (до вас)
  3. рекурсивно спадає у всі підкаталоги

Ви можете вважати імена шляхів у стилі UNIX. Кому взагалі потрібна ця програма на машині Windows?

Це кодовий гольф, виграє найкоротша програма (символи #ascii). Оскільки я так ненавиджу пробіли, кожен пробіл доводиться рахувати двічі.

Вкажіть, будь ласка, свою мову, бал, програму та короткий опис, як її запустити.

Програма повинна компілювати та виконувати з розумними зусиллями на моїй машині Linux.

EDIT: Оскільки Etan запитував структуру файлів для тестування, ось сценарій, який я використовую в даний час для створення відповідного дерева файлів:

#!/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
Це напрошується рішення, прийнятого без ascii chars.
Денніс Джахеруддін

50
Тепер я хочу навчитися
Whitespace

10
@BrunoJ, роблячи це в Whitespace, спочатку вимагатиме розробити систему доступу до файлів у WS. Я думаю, це було б складніше, ніж власне виклик.
Nzall

7
Чекаю, коли хтось опублікує рішення C / C ++, щоб я міг його вкрасти, компілювати, розміщувати у шістнадцятковій формі як машинний код x86 із пробілами ZERO! [або, можливо, base64]
Марк К Коуан

10
Я ненавиджу підкреслення у назви файлів. Використовуйте тире.
Доктор Ребму

Відповіді:


10

Zsh + GNU coreutils - 48 байт (1 пробіл)

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

Дивно, що ти ненавидиш пробіли (ASCII), але добре вкладаєш вкладки та нові рядки, але, мабуть, це займає всі види.

zmv вирішує безліч проблем із перейменуванням файлів стисло (і лише трохи неясно). Однак він наполягає на тому, щоб цілі були унікальними; в той час як ви можете легко додати унікальні суфікси, додавання суфікса лише в тому випадку, якщо це знадобиться в значній мірі вимагає повторної роботи всієї роботи. Тому замість цього я циклічно вручну і покладаюся на GNU mv, щоб додати унікальний ідентифікатор у разі зіткнення ( --backupопція, плюс --no-target-directoryу випадку, якщо ціль є існуючим каталогом, як інакше mvпереміщуватиме джерело всередині цього каталогу).

(od)є глобальним класифікатором для сортування результатів із каталогами, що з’являються після їх вмісту (як-от find -depth). Dвключає в себе точки файлів у глобальній точці. :hі :tє модифікаторами історії, подібними до dirnameта basename.

mvскаржиться, що його закликають перейменовувати файли на себе, оскільки глобул містить імена файлів без пробілів. Це життя.

Негольована версія:

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

1
це взагалі не перейменовує мої файли!
М.Герцкам

@ M.Herzkamp О, так, zmvбомби, перш ніж mvмає шанс розібратися в зіткненнях. Гаразд, я роблю це вручну. Виявляється, точно такої ж довжини, якщо я пропускаю точкові файли і навіть зберігаю символ, якщо цього не роблю.
Жиль

1
Зараз це працює. Btw: Я включив космічну кару в той момент, коли в мене справді була кривда на пробіли;) Як не дивно, я не виключав пробілів, коли розміщував виклик: P
M.Herzkamp

13

Bash 116 байт, 16 пробілів

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

Я не придушував помилок, щоб набрати ще пару байтів. Це не матиме жодних зіткнень.

Якщо findможна очікувати непозиційного GNU , це може бути скорочено далі:

Bash 110 байт, 15 пробілів

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

Для видалення пробілів замість їх заміни використовується два менших байти:

Bash 108 байт, 15 пробілів

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

Примітка: якщо замість пробілів можна використовувати вкладки, потрібен лише 1 пробіл (той, що знаходиться в правилі відповідності для заміни у рядку 2).

Дякуємо Деннісу за те, що він знайшов помилку в подвійній цитаті (і надає рішення)


11
ЧИ ДОБАВЛЯЄТЬСЯ ВНУТРІШНІЙ ПРОСТОР ТУТ, ЩО СМЕНИТИ МНЕ ??? ;-)
М.Герцкам

@ M.Herzkamp Я хоч і була помилкою копіювання та вставки, але насправді вона є. Здогадайтесь, я набрав ще 2 бали. Також -depthу GNU може бути замінено на -d, хоча він скаржиться на те, що він застарів. Я не знаю про правила гольфу, чи можу я це зробити?
pqnet

2
Поки це працює, я це дозволяю. Якщо депресія стане видаленням у майбутній версії, мені, можливо, доведеться повернутися до цієї відповіді та спростувати її, оскільки вона не є коректною ;-)
M.Herzkamp

2
Це не працюватиме належним чином, якщо будь-яка назва файлів містить подвійну цитату. Щоб виправити це, ви можете використовувати bash -c 'B=${0##*/}...' {} \;замість цього, що насправді коротше.
Денніс

3
Я думаю, я буду тим хлопцем, що з Nзмінною? Це ніколи не визначено ...
Стівен Пенні

9

Python 180 байт

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)

лише два пробіли, якщо ви використовуєте вкладку для відступу :-)


Я думаю, що більшість інших відповідей могли б покращити їх бал, використовуючи вкладки, а не пробіли.
kasperd

Але ваше представлення використовує пробіли, чи не так? (+1 для робочого коду)
Герцкамп

Я не знаю, як зафіксувати символи вкладки у відповідь ...
Емануеле Паоліні

2
замінено на вкладки :-)
Емануеле Паоліні

3
Як потворно ... Ну, мабуть, я про це просив :(
М.Герцкамп

5

Якщо в порядку замовлення суфіксів файлів не потрібно створювати прецедент попередньому файлу, для мене працює наступне:

bash / find / mv 84 байти, 16 пробілів

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

bash / find / mv 82 байти, 14 пробілів

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

Обнімається, &&щоб заощадити два пробіли.

bash / find / mv 60 байт, 11 пробілів

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

Захист від помилок, тому він отримує помилки від mv у файлах, у яких немає пробілів.

Редагувати: Випало цитати, {}як нагадав Денніс. Також дозволено findкричати про переносимість та застарілість у найкоротшій версії, де mvвже кричать про переміщення файлу поверх себе.

Редагування 2: Додано -Tдо mvкоманди, щоб уникнути вкладення каталогів замість перейменування, як вказувало pqnet. Використовується розширення дужок вартістю одного символу за допомогою простого простору.


Ви можете використовувати -dзамість, -depthі вам не потрібні цитати навколо {}.
Денніс

@Dennis Так. Я бачив -dрозмову у відповіді pqnet, але подумав, оскільки я замовчував mvкрики, щоб уникнути findкрику. Хоча я, мабуть, мушу скоротити це для кричучого. І так, я завжди {}чомусь цитую, хоча я знаю, що вам цього не потрібно. Сила звички я здогадуюсь.
Ітан Рейснер

1
Коли зіткнення відбудуться з іменами каталогів, вони помістять одне в інше (а не пробіли). Використовуйте -Tваріант, щоб mvуникнути цього
pqnet

Це працює, і я сказав у виклику, що додаток залежить від вас. +1
М.Герцкам

4

NodeJS - 209 байт, 3 пробіли

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

Я не знайомий з node.js. Як би я його запустив?
М.Герцкамп

Вам знадобляться виконувані вузли Node ; збережіть його у файл та запустітьnode file.js
cPu1

7
Я отримую виняток TypeError: Object #<Object> has no method 'exists'. Вгадайте, де: це в рядку 1! : D
М.Герцкамп

Я це протестував. У всякому разі, я замінив, що існує з його синхронним аналогом. Можна спробувати зараз?
cPu1

1
У мене встановлена ​​тільки версія 0.6.12. У цьому може бути проблема.
М.Герцкамп

2

Bash - 86 байт

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

Ой, подивимось
Subbeh

2
Також пробіли рахуються двічі ;-)
М.Герцкам

що саме ви маєте на увазі під пробілами, рахуються двічі?
Суббех

1
Ви можете зберегти багато символів, --backup--b

1
Так, зараз це працює і з моїм тестовим набором! +1
М.Герцкамп

2

Bash + Perl rename64

( renameце сценарій Perl для Debian та похідних, а не команда util-linux.)

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

11
Що станеться, якщо "my file.txt" та "my_file.txt" є присутніми?
М.Герцкам

1
О правда .. Працюю над цим незабаром
german_guy

1
*повинно бути {}, оскільки воно стоїть, це перейменовує лише файли, ім'я яких відображається у поточному каталозі. Це не додає суфікс у разі зіткнення. Ви можете трохи заощадити, опустивши, -name "* *"оскільки renameмовчки ігнорує файли, ім'я яких не трансформується.
Жиль

2

POSIX sh+ GNU find+ GNU mv67 байтів ASCII + один (буквальний) пробіл

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

Я не знаю, чи підходить вона, але при цьому будь-яка послідовність пробілів зміщується в єдине _- мені це все одно подобається. Насправді будь-яка послідовність, окрім провідної / кінцевої пробілів, - це автоматично усічені (що також, я думаю, сприятлива поведінка) . Дякую Жилсу, що вказав на це.

Для цього просто використовується внутрішній роздільник полів для розділення полів.

Це досить ... балакано ...

... о людино. Я знав, що річ із вкладки - дешева, але я подумав, що вона принаймні розумна. Зараз я просто спізнююся на вечірку ...


Це працює на моєму тестовому наборі, як ви задумали, але не так, як вимагає виклик. Мені це подобається, адже я, мабуть, навчусь чомусь новому. Напевно, мені доведеться прочитати цю IFSмагічну річ ...
Герцкамп

1
@ M.Herzkamp - ifs поводиться по-різному залежно від того, встановлено це пробіл чи ні. Більшість людей ненавидить це, оскільки вони не розуміють двох його первинних якостей - що він працює лише на розширення ( $expandне (ex pand)) та щойно згадану річ ifsws. Подивіться тут
mikeserv

Це не перейменовує файли всередині каталогів, імена яких містять пробіли. Виправлення було б замінити -execна -execdir. Ще одна химерність, IFSяку ви не згадуєте , - це те, що проміжки видаляються. Зауважте, що, як і інші зауважили, вам потрібна і ця -Tопція mv, коли ціль mvвиклику - це вже існуючий каталог.
Жиль,

@Gilles - моїм уподобанням було б використовувати sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;та інші глобуси в find -type dкоманді, щоб створити дзеркальне дерево жорстких посилань, а потім оперувати ними, але я вдруге здогадуюсь написати взагалі гольф коду для операції переміщення. Хороший момент щодо провідних / трейлінг-просторів, хоча я думаю, що це теж поведінка, яку я вважаю за краще.
mikeserv

@Gilles - але, до речі, це не примха - це навмисний і стандарти контрольованого поведінки. Розділ « Розділення поля» є одним з небагатьох у специфіці оболонки, який не містить слів, не визначених або визначених реалізацією . Там немає таких гарантій з zsh«s вбудовано функції zmv , наприклад.
mikeserv

2

PHP, 147 145 байт, 2 1 пробіл 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);}}}

рекурсивна функція. Бігайте зs(".");

Проведіть цикл globрезультатів для заданого шляху:

  • якщо каталог, повторити
  • замініть пробіли на підкреслення
  • якщо рядки відрізняються
    • під час прийняття нового імені файлу додайте підкреслення
    • перейменувати файл / каталог

php буде перейменовувати файли на сервері ... Тепер мені цікаво, як змінити імена файлів клієнта, коли вони відвідують ваш сайт: D
M.Herzkamp

1

Рубін 121

require 'find'

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

6
Ласкаво просимо в Code Golf! Ідея цих проблем із кодовим гольфом полягає у використанні найменшої кількості символів. Це означає , що ви можете точно позбутися порожніх рядків і вкладок, а також зробити імена змінних з одного символу, але люди дивляться на всякі з творчих способів , щоб зменшити кількість символів.
Не те, щоб Чарльз

Я отримую помилку, що Каталог не порожній: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

Пітон, 187

165, плюс 22 штрафні бали за пробіли.

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, використовуючи хитрість Емануеля \ t:

В цьому лише один пробіл!

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

Це працює для мене. +1
M.Herzkamp

видаліть пробіли на початку рядків і використовуйте вкладки - вони не пробіли, тому рахуйте лише один раз
chill0r

@ chill0r Ось яка друга версія; всі пробіли замінюються вкладками, крім одного (за винятком того, що SO все ще відображає їх як пробіли).
Генрі Кейтер

1

LiveScript - 166

(Замініть пробіли вкладками.)

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

На основі nderscore в оптимізованої версії з CPU1 «s відповіді .


Працює! +1 Я хочу видалити свої коментарі раніше, щоб прибрати цю публікацію.
М.Герцкамп

0

Bash 4+ 111 байт

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

1
Ті ж проблеми, що і кілька інших записів: Ви замінюєте пробіли у батьківських каталогах, а mv не може їх знайти. Також ви повинні змінити напрямок переходу, інакше ви перейменовуєте каталоги і mv не може знайти файли всередині.
Герцкамп

0

Groovy, 139 символів

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)

згідно з коментарем @ edc65

Groovy, справляйте зіткнення, 259 символів

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
Це не справляється зіткненнями.
edc65

Переконайтеся, що файли були перейменовані до того, як їхні батьківські каталоги, і що пробіли в батьківських каталогах не замінені.
М.Герцкамп

Я впевнений, що це нормально
авторизуйтесь

0

POSIX (випробувано на zsh) + основні команди Linux 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 Виправлено.
LinGeek

Кілька речей: яка функція експорту IFS та c в ls -cR? І яку версію телевізора вам потрібна для опції - відповідь? (У мене 8.13, і він не визнає варіант). Щоб отримати кращу оцінку, слід скоротити імена змінних.
М.Герцкам

C замінює пробіли новими рядками. IFS зупиняє пробіли як роздільники. - відповідь із старих версій і збирається виправити.
LinGeek

1
Ви пропускаєте другий телевізор у рядку 5? І я думаю, що одне відлуння в цьому рядку неправильне.
Герцкамп

1
$(ls -CR)є абсолютно фіктивним. Цей -cпараметр марний, і -Rви отримуєте файли без їх каталогу, що безглуздо. Ваша архітектура принципово не обробляє імена файлів, що містять нові рядки. Потрібно, set -fінакше імена файлів, що містять символи, вибухнуть. exportмарно. Я нечітко бачу, що ви намагаєтесь зробити, щоб уніфікувати файли, але трубопровід є неправильним.
Жиль
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.