为什么我的文件夹名称最终会这样,以及如何使用脚本解决此问题?


15

抱歉,如果在其他地方都可以找到答案,我不知道如何搜索我的问题。

我在redhat linux HPC服务器上运行了一些模拟,而我用于处理文件夹结构以保存输出的代码有一个不幸的错误。我创建文件夹的matlab代码是:

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

在哪里sp.run_number是整数。我忘了将其转换为字符串,但是由于某种原因mkdir(folder);(在matlab中)运行仍然成功。实际上,模拟运行顺利,数据已保存到匹配目录。

现在,当查询/打印文件夹结构时,出现以下情况:

  • 当我尝试标签自动完成时: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • 当我使用lsrun_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?
  • 当我使用rsync传输到我的mac时,该--progress选项显示:run_\#003/等(与我假设)匹配的数字与sp.run_number填充的整数匹配为三位数,因此第10次运行是run_\#010/
  • 当我在finder中查看文件夹时 run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • 查看问题并使用命令,ls | LC_ALL=C sed -n l我得到:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

我无法cd使用任何这些表示形式进入文件夹。

我有成千上万个这样的文件夹,因此需要使用脚本来解决。这些选项中的哪一个是文件夹的正确表示形式?如何以编程方式引用这些文件夹,以便使用bash脚本使用正确格式的名称重命名它们?而且我想出于好奇,这到底是怎么发生的?


4
“当我尝试选项卡自动完成时:...如果我尝试键入...”,为什么要输入而不让自动完成完成?也^A没有从字面上^其次A,但按Ctrl-A(可以用Ctrl-V CTRL-A键入它自Ctrl-A键通常是用于壳的快捷方式)。
muru

@muru无法正常工作...我得到了run_足够的帮助,我必须输入一些内容
Phill

抱歉,在我看到您的编辑之前发表了评论,该内容通过cd吸引了我
Phill


9
顺便说一句,在matlab中mkdir这样做的“某些原因”是因为在UNIX文件系统上文件或目录名中唯一的无效字符是NUL和正斜杠/。任何其他字符都是有效的,包括控制字符。我不知道如果sp.run_number为0(可能run_因为错误而中止或产生,因为NUL字节将终止目录名称字符串),matlab会怎么做。当然,这对于其中具有NUL字节的16位(或更高)值也将是有问题的,并且还会根据运行matlab的系统的字节序而变化。
cas

Answers:


26

您可以使用perl rename实用程序(aka prenamefile-rename)重命名目录。

注意:请勿将它与renamefrom util-linux或任何其他版本混淆。

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/

这使用perl ord()函数将文件名中的每个控制字符替换为该字符的序号。例如^A变为1,^B变为2,依此类推。

-n选项是干运行显示什么rename ,如果你让它做。删除它(或替换-v为详细输出)以实际重命名。

操作中的e修饰符s/LHS/RHS/eg使perl作为perl代码执行RHS(替换),并且$1是来自LHS的匹配数据(控制字符)。

如果要在文件名中使用零填充数字,则可以ord()与结合使用sprintf()。例如

$ rename -n 's/([[:cntrl:]])/sprintf("%02i",ord($1))/eg' run_*/ | sed -n l
rename(run_\001, run_01)$
rename(run_\002, run_02)$
rename(run_\003, run_03)$
rename(run_\004, run_04)$
rename(run_\005, run_05)$
rename(run_\006, run_06)$
rename(run_\a, run_07)$
rename(run_\b, run_08)$
rename(run_\t, run_09)$

以上示例 sp.run_number在您的matlab脚本中位于0..26的范围内时才起作用(因此,它在目录名称中生成了控制字符)。

要处理任何1个字节的字符(即从0..255开始),您可以使用:

rename -n 's/run_(.)/sprintf("run_%03i",ord($1))/e' run_*/

如果sp.run_number可能> 255,则必须使用perl的unpack()函数而不是ord()。我不完全知道matlab如何在字符串中输出未转换的int,因此您必须进行实验。有关perldoc -f unpack详细信息,请参见。

例如,以下代码将解压缩8位和16位无符号值,并将它们零填充到5位宽:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/

谢谢你的细节!我正在尝试使用该-n选项进行测试,但它告诉我它是一个无效的选项-版本信息提供了我的信息,rename from util-linux 2.23.2所以我不确定它是否具有相同的功能
Phill

3
这就是为什么我指定了该实用程序的perl版本renameutil-linuxrename完全不同,功能远远不够,并且命令行选项不兼容。如果您运行的是debian或类似操作系统,请尝试安装该file-rename软件包。否则,请为您的发行版安装适当的软件包。它可能已经安装,请尝试运行prenamefile-rename代替rename
cas

是的,我认为是这样。我看看是否可以让其中之一工作。再次感谢您抽出宝贵的时间来帮助我!
菲尔

11

而且我想出于好奇,这首先是怎么发生的?

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

在哪里sp.run_number是整数。我忘了将其转换为字符串,但是出于某种原因运行mkdir(folder);(在Matlab中)仍然成功。

因此,似乎mkdir([...])在Matlab中将数组的成员连接起来以将文件名构建为字符串。但是您给了它一个数字,而数字才是计算机上真正的字符。因此,当sp.run_numberwas时1,它会给您带值的字符1,然后给您带值的字符2,等等。

这些是控制字符,它们没有可打印的符号,并且在终端上打印它们还会带来其他后果。因此,它们通常由不同类型的转义符表示\001:(八进制),\x01(十六进制)^A是具有value的字符的常见表示1。值为零的字符有些不同,它是NUL字节,用于在C和Unix系统调用中标记字符串的结尾。

如果您高于31,则会开始看到可打印的字符,32是空格(虽然不是很明显),33 = !,34 = "等。

所以,

  • run_ run_^A/ run_^B/—第一个run_对应于零字节的字符串,字符串在此结束。其他显示您的shell喜欢使用显示控制代码^A。该符号还暗示了一个事实,即可以将数字值为1的char输入为Ctrl-A,尽管您需要告诉shell Ctrl-V Ctrl-A至少应在Bash中这样做,而不是将其解释为控制字符,而应将其解释为文字。

  • ls:run_ run_? run_?ls不喜欢在终端上打印无法打印的字符,而是将其替换为问号。

  • rsync:run_\#003/—这对我来说是新手,但是想法是一样的,反斜杠表示转义符,其余的是字符的数值。在我看来,这里的数字是八进制的,就像更常见的一样\003

  • 使用命令ls | LC_ALL=C sed -n l... run_\006$ run_\a$ run_\b$ run_\t$- \a\b\t为C分别逸出报警(钟形),退格键和退格。它们具有数值7、8和9,因此应该清楚为什么要使用它们\006。使用这些C转义标记是标记控制字符的另一种方法。尾随的美元符号表示行结束。

至于cd,假设我的假设是正确的,cd run_则应转到该目录,且结尾没有奇数字符,并且cd run_?应给出错误,因为问号是与任何单个字符匹配的glob字符,并且有多个匹配的文件名,但cd仅期待一个。

这些选项中的哪一个是文件夹的正确表示形式?

从某种意义上来说,所有这些...

在Bash中,可以使用引号中的\000\x00转义 $'...'符来表示特殊字符,因此$'run_\033(八进制)或$'run_\x1b'与字符值27(恰好是ESC)的目录相对应。(我不认为Bash支持使用十进制数字进行转义。)

cas的答案有一个脚本可以重命名这些脚本,因此我不会去那里。


如果是GNU ls,则有一些引号选项,包括-b/ --escape--quoting-style=,或QUOTING_STYLE环境变量,用于控制非打印字符的显示方式。不过,我认为没有选择让它更喜欢八进制转义而不是字符版本。
Toby Speight

3

最简单的方法是在发生事故的相同环境中创建错误的文件名和正确的文件名,然后将文件夹移动/重命名为正确的名称。

为了避免现有名称之间发生冲突,最好使用另一个目标文件夹。

./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3

如果可能的话,我更喜欢修复脚本并再次运行它。修复一些怪异的bug验尸可能会花费更多,并且可能会引入新的问题。

祝好运!

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.