处理带有特殊首字符的文件名(例如♫)


30

我最近遇到了一个名称以字符“ the”开头的文件。我想复制此文件,将其输入ffmpeg,并在终端中以其他各种方式引用它。我通常会自动完成怪异的文件名,但这会失败,因为我什至无法输入第一个字母。

我不想切换到鼠标来执行复制粘贴操作。我不想为可能的情况记住一堆代码。我的临时解决方案是切换到vim,粘贴!ls并复制有问题的字符,然后退出并将其粘贴到终端中。这行得通,但是非常可怕。

有没有更简单的方法来处理这种情况?

注意:如果使用鱼壳,它会改变一切。


7
您可以使用文件的其他部分来构成一个正则表达式来使用它吗?*restoffile.avi或类似的东西?
slm

1
在这种情况下,剩余的名称是汉字和片假名(日语脚本)的混合体,因此并不容易。
ZirconCode 2014年

3
明白了,只是以为我会问。吉米吉的答案能解决吗?您还介意粘贴有问题的文件的屏幕截图吗?这可能对其他可能会在以后阅读的内容有所帮助。
slm

1
我正在尝试使其立即运行。我不知道如何发布筛选,但运行以下命令将给您我的模拟问题:touch '♫ 漢字カ' touch '♫ 漢字タ'
ZirconCode 2014年

1
使用zsh,您可以使用选项使选项卡具有一个菜单,您可以从中选择适当的文件。
凯文(Kevin)

Answers:


35

如果文件名的第一个字符是可打印的,但是字母数字和空格都不能,则可以使用[[:punct:]]glob运算符:

$ ls *.txt
f1.txt  f2.txt  ♫abc.txt
$ ls [[:punct:]]*.txt
♫abc.txt

嗯,我对这些glob运算符一无所知,我阅读了一下并学到了一点(谢谢),它解决了我的问题,这是我的目录中只有一个奇怪的文件。文件,我应该问一个新问题还是更新这个问题?
ZirconCode 2014年

我已经接受了您的回答,明天有时间我会发布第二种情况。感谢您的帮助。
ZirconCode 2014年

6

对我来说ls [^a-zA-Z0-9]*,最简单的方法就是为我做这招,但是terdon的答案更好地是引起人们对extglob shell选项甚至与shell无关的方法的关注。


这是一个足够的刺伤。您可以ls [^[:alnum:]]*为同一件事。但最好使用字符类它,而不是类(ES),它不是 ; 因此ls [[:punct:]]*将列出此文件。
Rich

6

ls有一些开关(例如--quote-name,-escape,-literal)用于处理不可打印的字符,但是在这种情况下,该字符似乎是“可打印的”而不是“可键入的”(至少在我的键盘上!)。 ),因此这些开关似乎都无济于事。

因此,作为摆脱文件名中带有任何字符的文件的通用“蛮力”方法,您可以这样做:

$ /bin/ls -1A|cat -n  # list all files (except . and ..), 1 per line, add line numbers
     1  ♫
     2  f1.txt
     3  f2.txt

查找包含有问题的文件的行。很可能它将是第一行,但是我们可以说它是第五行。打印第5行并对其进行十六进制编码:

$ /bin/ls -1A|sed -n 5p|xxd -g 1
0000000: e2 99 ab 0a                                      ....

忽略0a(换行符)字符,构造转义字符串,并使用echo的-e选项转换转义:

$ echo -e '\xe2\x99\xab'
♫

现在,您可以像这样复制/移动/删除它:

$ cp -vi $(echo -e '\xe2\x99\xab') better_name
‘♫’ -> ‘better_name’

另外,如果您不限于使用Shell脚本,则可以在Python中执行以下操作:

$ python
>>> import os
>>> os.listdir('.')
[ ..., '\xe2\x99\xab', ... ]
>>> print '\xe2\x99\xab'
♫
>>> import shutil
>>> shutil.copy('\xe2\x99\xab', 'better_name')

使用这种方法,您可以处理许多文件,只需要编写逻辑来选择正确的文件,然后重命名它们而不会造成混乱,等等:

for f in os.listdir('.'):
  if not f.isalnum():
    newname = generate_newname(f)
    if not os.path.exists(newname):
      shutil.copy(f, newname)
    else:
      print newname, 'already exists!'

5

一种类似的方法是列出所有不以“正常”字符开头的文件。在bash中,您可以使用

$ shopt -s extglob
$ ls !([[:alpha:]]*)

但是,这似乎不适用于fish,因此您可以find改用:

$ find . -type f -not -name '[[:alpha:]]*'

4

重命名符号链接

处理带有特殊字符的文件名的一种方法- 重命名为更简单的名称,例如使用首字符或文件名中的其他字符。

即使您需要保留原始文件名,也可以使用它:重命名文件名的副本。
这可以通过复制文件来完成,也可以通过创建文件的符号链接或硬链接并重命名它们来完成。cp创建符号链接,而不是带有选项的副本-s-l用于硬链接)。

使用“排毒”清除名称

detox可以使用重命名来清理文件名;它根据文件中定义的各种规则重命名文件以清除文件名detoxrc。默认情况下,只删除了UTF8字符;如果选择,-s utf_8-only它们将被替换为_

$ touch '♫ 漢字カ' ♫foo
$ ls -1
♫foo
♫ 漢字カ
$ detox -s utf_8-only * 
$ ls -1                
_ ___
_foo


符号链接上的“排毒”

结合使用如上所述的符号链接:

$ mkdir orig
$ cd orig 
$ touch '♫ 漢字カ' ♫foo
$ cd ..
$ mkdir clean
$ cd clean 
$ cp -s ../orig/* .
$ ll               
lrwxrwxrwx 1 14 Oct  8 05:52 ♫foo -> ../orig/♫foo
lrwxrwxrwx 1 21 Oct  8 05:52 ♫\ 漢字カ -> ../orig/♫\ 漢字カ
$ ls -1
♫foo
♫ 漢字カ
$ detox --special -s utf_8-only *
$ ll                                
lrwxrwxrwx 1 21 Oct  8 05:52 _\ ___ -> ../orig/♫\ 漢字カ
lrwxrwxrwx 1 14 Oct  8 05:52 _foo -> ../orig/♫foo

2

我没有使用fish,但是文档说您可以通过在其十六进制字符代码前添加\u(对于16位字符)或\U(对于32位字符)来输入Unicode字符。我认为的代码491eb,因此您可以执行以下操作:

mv \U000491ebabc.mp3 abc.mp3

重命名♫abc.mp3

注意,您需要前导零,否则abc最后将被视为十六进制数字和部分字符代码;对于32位字符,您需要输入8位数字。


2

我不知道在2014年问问题时是否已经存在,但是在当前版本fish(截至2019年)中,您可以按Tab两次以获取zsh样式的选择,您可以使用箭头键来选择直观地选择所需的文件,而无需键入文件名的任何部分。


2

Fish不设计支持括号通配符 ¹。

function find_special_filename
    find ! -path './.*' -name '[^-.a-zA-Z0-9_]*' $argv
end

该命令不显示隐藏目录和文件名显示不以字符开始搜索lettersdigits. _ -(CF的文档find)。

注意: $argv是一个特殊的数组变量(Fish shell),其中包含函数参数,因此基础命令可以接收任何表达式(例如alias)。

find_special_filename -exec mv '{}' misc/ \;

¹ 实际上,Fish支持方括号扩展(数组变量扩展),但Bash使用另一种术语(参数和文件名扩展)。



0

您没有说是否要保留这些有问题的文件名。一种解决方案可能是通过将文件的(部分或全部)重命名为您可以通过运行以下脚本键入的名称来彻底“解决”该问题:

#!/bin/sh
for old in *
do
      printf "%s ...? " "$old"
      if read new  &&  [ "$new" != "" ]
      then
             mv -i "$old" "$new"
      fi
done

这将列出您现有的文件名,每个文件名后跟一个...?。只需键入Enter即可保留文件;或输入新名称来重命名。-i如果您指定另一个现有文件的名称,该选项将导致它要求您确认覆盖。

可以通过几种方式修改此脚本:

  • 您可以将通配符(*)修改为更具限制性的内容,例如*.avi *.mov,因此您不必查看每个文件。
  • 您可以将更mv改为cp,因此您可以保留文件的当前名称副本,并使用可键入的名称创建(临时?)副本。
  • 您可以创建一个基于现有文件名的新文件名。例如,

    if read pfx  &&  [ "$pfx" != "" ]
    then
            mv -i "$old" "$pfx$old"
    fi
    

    这样您就可以在旧名称前面打一个前缀。如果选择唯一前缀,则可以使用自动完成功能。

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.