mkdir、touch 等的 bash/shell 路径名扩展?

bat*_*tad 4 shell wildcards

因此,在bash大多数其他 shell 中执行此类操作将无法在子目录中创建多个子目录或文件...

mkdir */test
touch */hello.txt
Run Code Online (Sandbox Code Playgroud)

当然,实际上有很多方法可以做到这一点,我的首选方法是find在可能的情况下使用而不是使用for循环,主要是为了可读性。

但我的问题是,为什么上述方法不起作用?
据我所知,这是因为完整的目标文件/路径不存在,但如果我尝试使用mkdirtouch. 我一直只是继续前进,从未真正质疑过它。
但是有没有人对此有一个像样的解释来帮助我一劳永逸地理解?

G-M*_*ca' 8

您可能忽略的一点——很多人都遇到了问题,特别是如果他们在使用 *nix 之前有使用其他操作系统的经验时——是在许多其他操作系统中,命令行上的通配符通常会传递 给命令按照它认为合适的方式进行处理。例如,在 Windows 命令提示符中,

rename *.jpeg *.jpg
Run Code Online (Sandbox Code Playgroud)

而在 *nix 中,为了简化单个命令(例如,mv)的程序员的工作 ,通配符(未引用或转义时)由 shell 以独立于接口和通配符作为参数的命令的功能。称取文件名作为命令行参数的大多数程序期望这些文件存在(是的,mkdirtouch有例外这一规则,因为是mkfifomknod和,在部分程度上cplnmv,和rename;还有可能是其他人),所以它shell 将通配符扩展为不存在的文件的名称并没有真正意义。对于外壳(而且,我的意思是每个 shell – Bourne、bash、csh、fish、ksh、zsh 等)以不同的方式处理异常可能会太麻烦。


也就是说,有几种方法可以获得您想要的结果。如果你知道通配符要扩展成什么,而且不长,你可以用大括号扩展生成它:

touch {red,orange,yellow,green,blue,indigo,violet}/rgb.txt
Run Code Online (Sandbox Code Playgroud)

更通用的解决方案:

sh -c 'for arg do mkdir -- "$arg"/test; done' -- *
Run Code Online (Sandbox Code Playgroud)

Gilles帮助我找到了另一种在 bash 中做到这一点的方法。

rename *.jpeg *.jpg
Run Code Online (Sandbox Code Playgroud)

显然benbradley这里只是一个标识符;您可以使用任何名称(例如,任何单个字母)。我花了几次尝试才做到这一点,所以让我分解一下:

  • identifier=value 创建一个名为identifier (如果它不存在)的(标量)变量并将值分配value给它。例如,G=Man。您可以使用;来引用标量变量。例如,,或者更安全地,作为。例如,and可能未定义,但is和is 。$identifier$G${identifier}$Gage$Gilla${G}ageManage${G}illaManilla
  • identifier=(value1 value2)创建一个名为identifier (如果它不存在)的数组变量并将列出的值分配给它。例如,
      光谱=(红橙黄绿蓝靛紫)
      或者
        all_text_files=(*.txt)
        $name将引用第一个元素(因此相当无用)。您可以将任意元素引用为; 例如,是 和是。您可以使用和 来引用整个数组。${name[subscript]}${spectrum[0]}red${spectrum[4]}blue${name[@]}${name[*]}

        所以,这里是碎片:

            " ${本布拉德利[@] / % //测试} "
            • ${parameter/pattern/string} 扩展并替换with的最长匹配项。${parameter}patternstring
            • 如果pattern以 开头#,则它必须匹配 的扩展值的开头parameter。如果pattern以 开头%,则它必须匹配 的扩展值的末尾parameter。换句话说,
                % (the-rest_of_the_pattern)
                表现得像
                  (the-rest_of_the_regex) $
                  (是的,这似乎有点倒退)。所以 apattern只是 a% 就像一个正则表达式只是 a $- 它匹配输入字符串的末尾(搜索空间)。
                  • 而我使用string/test。所以这用/test (即,它附加/test到参数)替换了参数的结尾。
                  • 另一件 不直观的事情 是它总是以. 它不需要也不能以第三个 结束。因此, a in不能解释为分隔符,因此我们可以使用 a of 而无需转义.${parameter/pattern/string}}//stringstring/test/
                  • 如果parameter是一个以@*为下标的数组变量,则替换操作依次应用于数组的每个成员。
                  • 当您将数组引用为 (而不是)并将结果放在双引号中时,您可以保留数组元素的完整性(即,保留其中的空格和其他特殊字符),而无需将单独的元素组合成一个长字.${name[@]}${name[*]}