抱歉,如果其他地方有答案,我不知道如何搜索我的问题。
我在 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/
- 当我使用时
ls
:run_ 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 脚本以正确格式的名称重命名它们?我想出于好奇,这到底是怎么发生的?
答案1
您可以使用 perlrename
实用程序(也称为prename
或file-rename
)来重命名目录。
笔记:不要与rename
fromutil-linux
或任何其他版本混淆。
rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/
这使用 perl 的ord()
函数将文件名中的每个控制字符替换为该字符的序号。例如^A
变成 1,^B
变成 2,等等。
该-n
选项用于试运行以显示内容rename
会如果你允许的话就做吧。删除它(或将其替换-v
为详细输出)以实际重命名。
e
操作中的修饰符使s/LHS/RHS/eg
perl 将 RHS(替换)作为 perl 代码执行,并且 the$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_*/
答案2
我想出于好奇,这到底是怎么发生的?
folder = [sp.saveLocation, 'run_', sp.run_number, '/'];
其中
sp.run_number
是一个整数。我忘记将其转换为字符串,但由于某种原因正在运行mkdir(folder)
; (在matlab中)仍然成功。
因此,在 Matlab 中似乎mkdir([...])
连接数组的成员以将文件名构建为字符串。但你给了它一个数字,而数字就是计算机上真正的字符。所以,当sp.run_number
was时1
,它给你的是 value 的角色1
,然后是 value 的角色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_?
应该给出错误,因为问号是匹配任何单个字符的全局字符,并且有多个匹配的文件名,但cd
只有期待一个。
以下哪个选项是文件夹的正确表示?
所有这些,从某种意义上来说……
在 Bash 中,可以使用引号内的 \000
和转义符来表示特殊字符,因此(八进制)或对应于字符值为 27 的目录(恰好是 ESC)。 (我认为 Bash 不支持十进制数字的转义。)\x00
$'...'
$'run_\033
$'run_\x1b'
cas的答案有一个脚本可以重命名它们,所以我不会去那里。
答案3
最简单的方法是在发生事故的同一环境中创建错误的文件名和正确的文件名,然后将文件夹移动/重命名为正确的名称。
为了避免现有名称之间发生冲突,最好使用另一个目标文件夹。
./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3
如果可能的话,我更愿意修复脚本并再次运行它;事后修复一些奇怪的错误可能会花费更多,并且可能会带来新的问题。
祝你好运!