为什么我的文件夹名称会变成这样,我该如何使用脚本解决这个问题?

Phi*_*ill 15 ls directory cd-command shell-script mkdir

抱歉,如果这在其他地方有答案,我不知道如何搜索我的问题。

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

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];
Run Code Online (Sandbox Code Playgroud)

其中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_$
Run Code Online (Sandbox Code Playgroud)

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

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

cas*_*cas 26

您可以使用 perlrename实用程序(又名prenamefile-rename)来重命名目录。

注意:不要与renamefromutil-linux或任何其他版本混淆。

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/
Run Code Online (Sandbox Code Playgroud)

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

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

操作中的e修饰符s/LHS/RHS/eg使 perl 将 RHS(替换)作为 perl 代码执行,并且$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)$
Run Code Online (Sandbox Code Playgroud)

当且仅当 sp.run_number您的 matlab 脚本在 0..26 范围内时(因此它在目录名称中产生控制字符),上述示例才有效

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

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

如果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_*/
Run Code Online (Sandbox Code Playgroud)

  • 这就是为什么我指定了 `rename` 实用程序的 **perl** 版本。`util-linux` 的 `rename` 非常不同,功能更差,并且命令行选项不兼容。如果您运行的是 debian 或类似版本,请尝试安装 `file-rename` 包。否则为您的发行版安装适当的软件包。它可能已经安装,尝试运行 `prename` 或 `file-rename` 而不是只运行 `rename`。 (3认同)

ilk*_*chu 11

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

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];
Run Code Online (Sandbox Code Playgroud)

其中sp.run_number是一个整数。我忘记将其转换为字符串,但由于某种原因运行mkdir(folder); (在matlab中)仍然成功。

因此,mkdir([...])在 Matlab 中似乎将数组的成员连接起来以将文件名构建为字符串。但是你给它一个数字,数字就是计算机上的字符。所以,当sp.run_numberwas 时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 的字符输入为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\x00转义 $'...'符来表示特殊字符,因此$'run_\033(八进制)或$'run_\x1b'对应于字符值为 27(恰好是 ESC)的目录。(我认为 Bash 不支持使用十进制数进行转义。)

cas 的答案有一个脚本来重命名它们,所以我不会去那里。