【问题标题】:How to extract substring from directory name using bash如何使用bash从目录名中提取子字符串
【发布时间】:2020-02-19 17:00:02
【问题描述】:

我有一组具有以下名称模板的目录:

[数字和/或字符和/或符号的随机组合(包括空格)]+[单个空格]+[(]+[数字]+[)]。

例如 asdf%k123 test1 (12345) foo(123)??bar (456) 324(asd)! (456) random()123 (456)

如何使用 bash 命令(不带“()”)仅提取括号内的数字?请记住,括号内的数字可能出现在名称的随机部分,但我只需要最后的 on。在任何情况下,每个名称都将以 [空格][(][数字][)] 结尾。

【问题讨论】:

  • 给定名称asdf%k123 test1 (12345)(根据您的示例)是您的预期结果12345?...假设您有foo(123)??bar(456)789 quux (54321)是您的预期结果54321?我建议您edit您的问题提供几个源名称示例以及每个示例的预期结果。
  • 谢谢你的建议,我有点模棱两可。是的,我只需要最后一对 () 中的数字。

标签: windows bash file


【解决方案1】:

获取号码

您可以通过sed获取最后一个号码:

sed 's/.*(\([0-9]*\))$/\1/' <<< "asdf%k123 test1 (12345)"

12345

sed 脚本是这样写的:

  • 开头s 表示执行替换,使用以下语法s/pattern/replacement/
  • / 字符定义了替换的分隔符,您可以使用任何字符,但 / 是一个非常常见的字符
  • .*(\([0-9]*\))$ 是模式(我稍后会回复)
  • \1 是替换,在这种情况下它替换模式捕获的第一个字符串,即数字(见下文)

关键元素是模式。在这种情况下,.*(\([0-9]*\))$ 可以拆分为:

  • .* 匹配任意字符任意次数
  • ( 匹配左大括号字符
  • \([0-9]*\) 捕获任何由数字组成的字符串,也就是数字
  • ) 匹配右大括号字符
  • $ 匹配行尾

在捕获模式中,即(\([0-9]*\),需要注意\(\)是捕获的分隔符,不要误认为()是正则括号字符。

TL;DR:此模式表示“我想在行尾之前捕获一个用括号括起来的数字”。 sed 脚本说“我想只打印捕获的号码”。

列出目录

您可以使用find 解析目录。如果您只想要一级子文件夹:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d

-mindepth 1 -maxdepth 1 选项可确保您仅获得 1 深度的子级,而 -type d 选项仅列出目录(不列出文件,不列出符号链接等)。

您可以通过将-maxdepth 1 替换为您选择的数字来获得更多深度,或者干脆省略此选项以递归方式获取所有子文件夹。

由于您的目录似乎包含各种特殊字符,我还建议使用 -print0 选项获取它们,该选项将结果与空字符 \0 分隔,而不是换行符。

解决方案

总而言之,它看起来像这样:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d -print0 |
  while IFS= read -r -d '' dirname
  do
    sed 's/.*(\([0-9]*\))$/\1/' <<< $dirname
  done

如果您想过滤掉与您的模式匹配的目录,您可以将 sed 更改为使用-n 选项,然后使用p 命令打印:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d -print0 |
  while IFS= read -r -d '' dirname
  do
    sed -n 's/.*(\([0-9]*\))$/\1/p' <<< $dirname
  done

【讨论】:

  • 对 powershell 没有太多经验,我在尝试运行它时不断出错。 “在执行循环中缺少语句主体”和“重定向运算符后缺少文件规范”。可能是我这边犯了一些愚蠢的错误:/
  • 未引用的$dirname 是造成麻烦的原因。
  • @Bjergsen 我以为你的问题是关于 bash,现在你提到了 PowerShell。你到底用的是哪一个?
  • 我在输入抱歉时没有考虑。我正在使用 Cygwin 运行 bash 脚本。
  • 来自 Cygwin 的 @Bjergsen Bash 很好。只是当您提到 PowerShell 时,您似乎正在尝试运行 PowerShell 脚本。
【解决方案2】:

请您尝试以下方法;

pat='[[:blank:]]\(([[:digit:]]+)\)/$'   # regex pattern explained below
path="."                                # or specify to the path where the directories exist
for d in "$path"/*/; do                 # pick directories in the $path
    if [[ $d =~ $pat ]]; then           # if the directory name matches the pattern
        echo "${BASH_REMATCH[1]}"       # then print the extracted number
    fi
done

提供示例的结果:

456
12345
456
456

正则表达式模式[[:blank:]]\(([[:digit:]]+)\)/$ 匹配子字符串,例如:

  • 一个空格[[:blank:]]
  • 后跟左括号\(
  • 后跟一个或多个数字的序列([[:digit:]]+)
  • 后跟右括号\)
  • 后跟一个斜杠(表示它是一个目录)/
  • 和字符串结尾$

数字序列被括号包围,因此匹配的 部分被捕获并分配给 bash 变量BASH_REMATCH

【讨论】:

  • 我已将代码复制到 bash 文件中,并将其放在包含文件夹中,但是当我运行它时,出现以下错误:$bash test.sh test.sh: line 7: syntax error near unexpected token done' test.sh: 第 7 行:done'
  • 如果你是在 Windows 环境下使用 Windows 文本编辑器运行代码,你可能有一个错误的行尾,不是 而是 。如果是这样,请尝试dos2unix 更正问题。
  • 我用的是Cygwin环境,所以不知道是什么问题。
  • 即使使用 Cygwin,结束代码 也可能会导致问题,具体取决于 bash 版本。是否可以使用od 或其他二进制转储工具来检查您的 bash 脚本文件的结束代码?
猜你喜欢
  • 2014-05-15
  • 2023-03-17
  • 2012-09-24
  • 2012-10-25
  • 1970-01-01
  • 2011-01-19
  • 2021-10-31
  • 2015-04-26
相关资源
最近更新 更多