因此,在大多数其他 shell 中执行类似的操作bash
将无法在子目录中创建多个子目录或文件...
mkdir */test
touch */hello.txt
当然,实际执行此操作的方法有很多,我首选的方法是在可能的情况下使用find
而不是使用for
循环,主要是为了可读性。
但我的问题是,为什么上面的方法不起作用?
据我了解,这是因为完整的目标文件/路径不存在,但如果我尝试mkdir
或,这肯定是一件好事touch
。我总是继续前进,从未真正质疑过它。
但是有人对此有一个体面的解释来帮助我一劳永逸地理解吗?
答案1
您可能忽略的一点是,许多人都遇到麻烦,特别是如果他们在使用 *nix 之前有其他操作系统的经验,那就是,在许多其他操作系统中,通常会传递命令行上的通配符 到命令按其认为合适的方式进行处理。例如,在 Windows 命令提示符中,
rename *.jpeg *.jpg
然而,在 *nix 中,为了简化各个命令(例如 mv
)的程序员的工作,通配符(当未引用或转义时)由 shell 以独立于接口和方式处理。通配符作为参数的命令的功能。大多数将文件名作为命令行参数的程序都期望这些文件存在(是的,mkdir
并且touch
是该规则的例外,如mkfifo
、mknod
、 和 在某种程度上,cp
、ln
、mv
和rename
;并且可能还有其他文件),因此它对于 shell 将通配符扩展为不存在的文件的名称实际上没有意义。对于外壳(我的意思是每一个shell – Bourne、bash、csh、fish、ksh、zsh 等)以不同方式处理异常可能会很麻烦。
也就是说,有几种方法可以获得您想要的结果。如果您知道通配符将扩展为什么,并且它不长,则可以使用大括号扩展来生成它:
touch {red,orange,yellow,green,blue,indigo,violet}/rgb.txt
更通用的解决方案:
sh -c 'for arg do mkdir -- "$arg"/test; done' -- *
吉尔斯帮助我找到了另一种在 bash 中做到这一点的方法。
benbradley=(*)
mkdir "${benbradley[@]/%//test}"
显然benbradley
这里只是一个标识符;您可以使用任何名称(例如,任何单个字母)。我花了几次尝试才把它弄好,所以让我把它分解一下:
identifier=value
创建一个名为identifier
(如果尚不存在)的(标量)变量并value
为其分配值。例如,G=Man
。您可以使用;引用标量变量例如,,或者,更安全的是, as 。例如,and可能未定义,但is和is 。$identifier
$G
${identifier}
$Gage
$Gilla
${G}age
Manage
${G}illa
Manilla
identifier=(value1 value2 … )
创建一个名为的数组变量identifier
(如果它尚不存在)并将列出的值分配给它。例如,
光谱=(红橙黄绿蓝靛紫)或者
all_text_files=(*.txt)
$name
将引用第一个元素(因此相当无用)。您可以将任意元素引用为;例如,is 和is 。您可以使用和 来引用整个数组。${name[subscript]}
${spectrum[0]}
red
${spectrum[4]}
blue
${name[@]}
${name[*]}
所以,这里是碎片:
“ ${ Benbradley[@] / % / /测试 } "
${parameter/pattern/string}
扩展并替换 的最长匹配项。${parameter}
pattern
string
- 如果
pattern
以 开头#
,则它必须在扩展值的开头匹配parameter
。如果pattern
以 开头%
,则它必须在扩展值的末尾匹配parameter
。换句话说,
%(模式的其余部分)行为就像
(正则表达式的其余部分)$(是的,这看起来有点倒退)。因此,a
pattern
只是 a%
就像一个正则表达式,它只是 a $
– 它匹配输入字符串的末尾(搜索空间)。
- 我正在使用
string
一个/test
.因此,这将参数的末尾替换为/test
(即,它附加/test
到参数)。 - 另一件 不直观的事情是
${parameter/pattern/string}
总是}
以.结尾。不需要,并且不能,以第三个结尾/
。因此,一个/
在string
不能被解释为分隔符,因此我们可以有一个string
的/test
而不需要逃避/
. - 如果是下标为or 的
parameter
数组变量,则替换操作将依次应用于数组的每个成员。@
*
- 当您将数组引用为 (而不是)并将结果放在双引号中时,您可以保留数组元素的完整性(即,保留其中的空格和其他特殊字符),而无需将单独的元素组合成一个长单词。
${name[@]}
${name[*]}
答案2
在调用命令之前,shell 会扩展通配符模式。看G-Man的回答以获得完整的解释。
大多数 shell 需要某种形式的中间命令才能对匹配项应用文本转换。 Zsh 提供了一种即时改变比赛的方法全局限定符:
e
或者+
为每个匹配执行代码,可以通过修改REPLY
字符串来改变匹配(甚至可以通过修改数组来改变匹配的数量reply
):
随后是一个历史扩展修改器应用一些内置转换
例子:
mkdir */(:s:/:/test:) # takes advantage of the fact that each match ends with /, which it replaces by /test
mkdir *(/e:REPLY+=/test:)
答案3
这不起作用,因为 shell 试图扩展全部的*/test
尚不存在的字符串。默认情况下,字符串会扩展为自身。如果您使用 Bash,您可能想查看各种通配选项以您想要的方式处理这个问题。