我讨厌文件名中的空格


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

22
这是乞求无ASCII字符的解决方案。
丹尼斯·贾赫鲁丁

50
现在,我想学习空白
BrunoJ,2014年

10
@BrunoJ在Whitespace中执行此操作首先需要您在WS中开发文件访问系统。我认为这将比实际挑战更具挑战性。
Nzall 2014年

7
等待某人发布C / C ++解决方案,以便我可以窃取它,进行编译,以零空间将它们作为x86机器代码发布![或base64]
马克·科恩

10
我讨厌文件名中的下划线。使用破折号。
Rebmu博士2014年

Answers:


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历史修饰符相似dirnamebasename

mv抱怨称它被称为将文件重命名为它们自己,因为glob包含不带空格的文件名。这就是生活。

非高尔夫版本:

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

1
这根本不会重命名我的文件!
M.Herzkamp 2014年

@ M.Herzkamp哦,对了,zmv炸弹爆炸前mv有机会解决碰撞。好的,我是手动进行的。如果我跳过点文件,甚至不保存字符,结果将是完全相同的长度。
吉尔斯2014年

1
现在可以了。顺便说一句:在我确实对空间充满怨恨之时,我还包括了空间罚款;)具有讽刺意味的是,当我发布挑战时,我并未排除空间:P
M.Herzkamp 2014年

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行的空格)。

感谢Dennis查找双引号中的错误(并提供解决方案)


11
后面有多余的空间可以找我吗???;-)
M.Herzkamp 2014年

@ M.Herzkamp我虽然是复制和粘贴错误,但实际上确实存在。猜猜我又获得2分。另外,-depth在GNU中可以用替换-d,尽管它抱怨它已被弃用。我不知道高尔夫规则,我可以吗?
pqnet 2014年

2
只要有效,我允许。如果过时的
折旧

2
如果任何文件名包含双引号,则将无法正常工作。要解决此问题,可以改用bash -c 'B=${0##*/}...' {} \;实际上更短的方法。
丹尼斯

3
我想我会是那个家伙,N变量到底是怎么回事?它从未定义过……
Steven Penny

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个空格:-)


我猜大多数其他答案也可以通过使用制表符代替空格来提高得分。
kasperd 2014年

但是您提交的内容使用空格吗?(工作代码为+1)
M.Herzkamp 2014年

我不知道如何在答案中输入制表符...
Emanuele Paolini 2014年

2
用标签替换:-)
Emanuele Paolini 2014年

3
多么丑...好吧,我想我要了:(
M.Herzkamp 2014年

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:添加-Tmv命令中以避免嵌套目录,而不是如pqnet所指出的重命名。使用花括号扩展仅需使用一个空格即可,而仅花费一个字符即可。


您可以使用-d而不是,-depth而您不需要引号{}
丹尼斯

@丹尼斯是的。我-d在pqnet的答案上看到了对话,但想了想,因为我正在降低mv尖叫声,所以我会避免find尖叫声。虽然我可能应该将它缩短为尖叫的那个。是的,{}尽管我知道您在这种情况下不必这么做,但由于某些原因,我总是引用。我猜习惯的力量。
伊坦·赖斯纳

1
当目录名称发生冲突时,它将一个放入另一个(而不是空格)。使用-T选项来mv避免这种情况
pqnet 2014年

这行得通,我在挑战中说,附录由您决定。+1
M.Herzkamp 2014年

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不熟悉。我将如何运行?
M.Herzkamp 2014年

您将需要Node可执行文件nodejs;将其保存在文件中并运行node file.js
cPu1

7
我例外TypeError: Object #<Object> has no method 'exists'。猜猜哪里:它在第1行中!:D
M.Herzkamp 2014年

我已经测试过了 无论如何,我用它的同步副本替换了现存的。现在可以尝试吗?
cPu1 2014年

1
我只安装了0.6.12版本。那可能是问题所在。
M.Herzkamp 2014年

2

重击-86字节

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

哎呀,会看看
Subbeh

2
此外,空格被计算两次;-)
M.Herzkamp 2014年

您对空格两次计数的确切含义是什么?
Subbeh 2014年

1
通过缩写--backup--b

1
是的,现在它也可以与我的测试仪一起使用!+1
M.Herzkamp

2

Bash + Perl rename64

rename是Debian及其衍生产品上的Perl脚本,不是util-linux命令。)

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

11
如果同时存在“我的file.txt”和“ my_file.txt”怎么办?
M.Herzkamp

1
哦,是的。.很快就进行工作
german_guy 2014年

1
*应该是{},因为它只会重命名名称出现在当前目录中的文件。如果发生冲突,则不会添加后缀。-name "* *"由于可以rename忽略名称未转换的文件,因此可以省去很多时间。
吉尔斯2014年

2

POSIX sh+ GNU find+ GNU mv67 ASCII字节+一个(文字)空间

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

我不知道是否合身,但任何与此序列的空间会被省略,以一个单一的_-我喜欢也无妨。实际上,除了前导/尾随空格之外的任何序列都是-那些会被自动截断(我认为这也是有益的行为)。感谢Gilles指出这一点。

这仅使用内部字段分隔符来分隔字段。

相当... 健谈 ...

...天啊。我知道标签的东西很便宜,但我认为它至少很聪明。现在我刚参加晚会...


这可以按您的预期在我的测试集上运行,但不是挑战所要求的。我喜欢它,因为我可能会学到一些新东西。我想我将不得不读这个IFS神奇的东西……
M.Herzkamp 2014年

1
@ M.Herzkamp-ifs的行为取决于是否将其设置为空白。大多数人讨厌它,因为他们不了解它的两个主要特质-它仅在扩展上运行$expand不能扩展和刚才提到的ifsws东西。看这里
mikeserv

这不会重命名名称包含空格的目录内的文件。解决方法是将替换-exec-execdirIFS您没有提到的另一个怪癖是删除了尾随空格。请注意,正如其他人注意到的那样,当呼叫目标是现有目录时,您也需要-T选择。mvmv
吉尔斯2014年

@Gilles-我的喜好是sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \;find -type d命令上使用和其他glob创建镜像的硬链接树,然后对它们进行操作,但是我第二次猜测是为移动操作编写了一个代码高尔夫球。关于前导/尾随空格的要点,尽管我认为这也是我更喜欢的一种行为。
mikeserv

@Gilles-但顺便说一下,这不是一个怪癖 -它是有意的且受标准控制的行为。该场分裂节中在shell规范不包含的话很少不明实现定义。例如,zsh内置函数 不提供此类保证zmv
mikeserv 2014年

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给定路径的结果:

  • 如果目录,则递归
  • 用下划线替换空格
  • 如果字符串不同
    • 当使用新文件名时,请在下划线后加上
    • 重命名文件/目录

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!在这些代码高尔夫球挑战中,这里的想法是使用最少的字符。这意味着,你绝对可以摆脱空行和标签,使变量名中的单个字符,但人们寻找各种各样创造性的方法,以减少字符数。
不是查尔斯(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
M.Herzkamp 2014年

1

Python,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,使用Emanuele的 \ 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答案


作品!+1我将删除我的评论以整理这篇文章。
M.Herzkamp

0

重击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无法在其中找到文件。
M.Herzkamp 2014年

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 2014年

确保在重命名文件之前先重命名文件,并且不替换父目录中的空格。
M.Herzkamp 2014年

我确定可以
登录

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和ls -cR中的c的功能是什么?而--reply选项需要什么版本的mv?(我有8.13,并且它不识别该选项)。同样,为了获得更好的分数,您应该缩写变量名。
M.Herzkamp 2014年

c用换行符替换空格。IFS停止空格作为分隔符。--reply来自旧版本,即将修复。
LinGeek

1
您是否在第5行中错过了第二个电视?我认为该行中的一个回声是错误的。
M.Herzkamp 2014年

1
$(ls -CR)完全是假的。该-c选项无用,并且-R使您的文件没有目录,这是没有意义的。您的架构从根本上不会处理包含换行符的文件名。您需要set -f,否则包含通配符的文件名将会爆炸。export是没有用的。我可以隐约看到您要做什么来唯一化文件,但是管道是错误的。
吉尔斯2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.