Я ненавижу пробелы в именах файлов

61

Это просто. Я терпеть не могу, когда люди используют пробелы при именовании файлов. Иногда это портит консольные команды и делает вывод ls безобразным.

Задача состоит в том, чтобы написать программу (только символы ascii), которая

  1. переименовывает все файлы (включая каталоги) в текущем каталоге в версии с удаленными пробелами или замененными на '_'
  2. при столкновении необходимо добавить уникальный идентификатор (до вас)
  3. спускается рекурсивно во все подкаталоги

Вы можете принять имена путей в стиле UNIX. Кому нужна эта программа на компьютере с Windows?

Это код гольф, выигрывает самая короткая программа (#ascii символов). Так как я очень ненавижу пробелы, каждый пробел должен быть посчитан дважды.

Пожалуйста, укажите свой язык, оценку, программу и краткое описание того, как ее запустить.

Программа должна компилироваться и выполняться с разумными усилиями на моем компьютере с Linux.

РЕДАКТИРОВАТЬ: Поскольку 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
M.Herzkamp
источник
22
Это требует решения, сделанного без ascii символов.
Деннис Джаэруддин
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)является префиксом glob для сортировки выходных данных с каталогами, появляющимися после их содержимого (например, 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
Теперь это работает. Кстати, я включил космический штраф в то время, когда я действительно испытывал недовольство пробелами;) Как ни странно, я не исключал пробелы, когда отправлял вызов: 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можно ожидать не-posix 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).

Спасибо Деннису за то, что он нашел ошибку в двойных кавычках (и предоставил решение)

pqnet
источник
11
ЧТО ТАКОЕ ВНЕШНЕЕ КОСМОС НАЙТИ, ЧТОБЫ СОХРАНИТЬ МЕНЯ ??? ;-)
M.Herzkamp
@ M.Herzkamp Я думал, что это была ошибка копирования и вставки, но на самом деле она есть. Думаю, я набрал еще 2 очка. Также -depthв GNU можно заменить -d, хотя жалуется, что это устарело. Я не знаю о правилах гольфа, могу ли я это сделать?
pqnet
2
Пока это работает, я позволю это. Если в будущих версиях устаревание станет удалением, возможно, мне придется вернуться к этому ответу и понизить его рейтинг за неправильность ;-)
М.Херцкамп,
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)

только 2 пробела, если вы используете табуляцию для отступа :-)

Эмануэле Паолини
источник
Я думаю, что большинство других ответов могли бы улучшить свой счет, используя также табуляции вместо пробелов.
Касперд
Но ваше представление использует пробелы, не так ли? (+1 за рабочий код)
M.Herzkamp
Я не знаю, как соотносить символы табуляции в ответе ...
Эмануэле Паолини,
2
заменены на вкладки :-)
Эмануэле Паолини
3
Как некрасиво ... Ну, наверное, я просил об этом :(
M.Herzkamp
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и вам не нужны кавычки {}.
Деннис
@ Деннис Да. Я видел -dразговор в ответе pqnet, но решил, что, так как я заставил себя замолчать mvкриком, я бы избежал findкрика. Хотя, наверное, мне стоит сократить его до кричащего. И да, я всегда цитирую {}по какой-то причине, хотя знаю, что в этом случае нет необходимости. Сила привычки, наверное.
Этан Рейснер
1
Когда происходит столкновение с именами каталогов, оно помещает одно в другое (а не лишает пробелов). Используйте -Tопцию, чтобы mvизбежать этого
pqnet
Это работает, и я сказал, что приложение зависит от вас. +1
M.Herzkamp
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('.');
CPU1
источник
Я не знаком с node.js. Как бы я это запустил?
М.Херцкамп,
Вам понадобится исполняемый файл Node Nojs ; сохраните его в файл и запуститеnode file.js
cPu1
7
Я получаю исключение TypeError: Object #<Object> has no method 'exists'. Угадайте, где: это в строке 1! : D
M.Herzkamp
Я проверил это. Во всяком случае, я заменил существующий с его синхронным аналогом. Можешь попробовать сейчас?
cPu1
1
У меня установлена ​​только версия 0.6.12. Это может быть проблемой.
M.Herzkamp
2

Баш - 86 байт

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done
Subbeh
источник
Ой, посмотрим
Subbeh
2
Кроме того, пробелы учитываются дважды ;-)
M.Herzkamp
что именно ты имеешь ввиду с пробелами посчитаны дважды?
Subbeh
1
Вы можете сохранить много символов, сокращенно --backupдо--b
1
Да, теперь это работает и с моим тестовым набором! +1
M.Herzkamp
2

Bash + Perl rename64

( renameэто скрипт Perl для Debian и производных, а не команда util-linux.)

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;
german_guy
источник
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"    "$*"'   {}  \;

Я не знаю, подходит ли это, но с этим любым последовательность пробелов сводится к одному _- мне все равно нравится. На самом деле любая последовательность, кроме начальных / конечных пробелов, то есть те, которые автоматически усекаются (что, я думаю, также является полезным поведением) . Спасибо Жилю за указание на это.

Это просто использует внутренний разделитель полей для разделения полей.

Это довольно ... болтливый ...

...о чувак. Я знал, что вкладка была дешевой, но я думал, что это по крайней мере умно. Теперь я просто опаздываю на вечеринку ...

mikeserv
источник
Это работает на моем тестовом наборе, как вы и предполагали, но не так, как того требует задача. Мне это нравится, хотя, потому что я, вероятно, узнаю что-то новое. Я думаю, мне придется прочитать эту 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
@ Жиль - но, кстати, это не причуда - это намеренное и контролируемое стандартами поведение. Раздел Field-Splitting является одним из немногих в спецификации оболочки, который не содержит слов не указано или не определяется реализацией . Там нет таких гарантий с zsh«s встроено функции zmv , например.
mikeserv
2

PHP, 147 145 байт, 2 1 пробел с -> 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 результаты для данного пути:

  • если каталог, рекурс
  • заменить пробелы подчеркиванием
  • если строки отличаются
    • пока новое имя файла занято, добавьте подчеркивание
    • переименовать файл / каталог
Titus
источник
php переименует файлы на сервере ... Теперь мне интересно, как изменить имена файлов клиента, когда они посещают ваш сайт: D
M.Herzkamp
1

Рубин 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end
gam3
источник
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, используя трюк Эмануэле :

Только один пробел в этом!

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 Это и есть вторая версия; все пробелы заменяются табуляцией, кроме одной (за исключением того, что 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 ответа .

nyuszika7h
источник
Работает! +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
Убедитесь, что файлы переименованы, прежде чем их родительские каталоги, и что пробелы в родительских каталогах не заменяются.
M.Herzkamp
Я уверен, что все в порядке
войдите
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
LinGeek
источник
@ M.Herzkamp Исправлено.
LinGeek
Несколько вещей: какова функция экспорта IFS и c в ls -cR? А какая версия mv вам нужна для опции --reply? (У меня 8.13, и он не распознает опцию). Также, чтобы получить лучший результат, вы должны сокращать имена переменных.
М.Херцкамп,
C заменяет пробелы символами новой строки. IFS перестает быть пробелами, являющимися разделителями. --Reply взят из старых версий и собирается быть исправленным.
LinGeek
1
Вам не хватает второго мв в строке 5? И я думаю, что одно эхо в этой строке неправильно.
М.Херцкамп,
1
$(ls -CR)полностью фальшивый -cОпция бесполезна, и -Rполучает вас файлы без их каталога, который бессмысленно. Ваша архитектура принципиально не будет обрабатывать имена файлов, содержащие переводы строк. Вам нужно, set -fиначе имена файлов, содержащие подстановочные знаки, взорвутся. exportбесполезно. Я смутно вижу, что вы пытаетесь сделать, чтобы унифицировать файлы, но трубка неправильная.
Жиль "ТАК - перестать быть злым"