如何正确工作?
下一行有效,但需要在"for"中使用参数。
for f in ~/files/*/*.txt do
代码:
list ()
{
for f in $1; do
echo $f
done
}
list "~/files/*/*.txt"
list "~/files/*.txt"
输出:
~/files/*/*.txt
~/files/*.txt
问题在于bash中的扩展顺序。
你的所作所为有一定的道理。例如,它将与list "./dir/*/*.txt"
一起工作。
您将列表参数用双引号括起来,这样在调用列表时就不会进行模式扩展。因此,1美元;列表";函数的字面意思是CCD_ 2。正如你所希望的那样。然后,在for指令中不加上$1,这样它就被扩展为一个文件名列表。所以它做你想做的事。或者几乎是这样。
问题是扩展的顺序是:
~然后是参数,然后是模式(加上其他不相关的(
因此,当1美元被它的价值所取代时,就太晚了。
如果你这样做了(根本不是一个建议。只是一个解释(:
list ()
{
for f in ~/$1; do
echo $f
done
}
list "files/*/*.txt"
list "files/*.txt"
它会起作用的。显然不是一个解决方案。但它有助于理解发生了什么:CCD_;列表";。然后for f in ~/$1
转化为for f in /home/you/$1
[~取代]然后转化为for f in /home/you/files/*.txt
[参数替换]然后转化为for f in /home/you/files/a.txt /home/you/files/b.txt
[模式扩展]
现在,对于解决方案,引用参数,然后使用$@,正如评论中所建议的那样,确实可以做到这一点。如果你不引用论点并打电话list ~/files/*.txt
然后在调用之前进行扩展。list ~/files/*.txt
转化为list /home/you/files/*.txt
然后信息CCD_ 11。然后传递到列表。但,在列表中,你们有两个论点。因此,实际上,对于for,那么,您需要使用"$@"
list ()
{
for f in "$@"; do
echo $f
done
}
list ~/files/list.txt
另一种方式,如果你有理由希望膨胀发生在"内部";列表";(例如,如果您可能想传递其中两个模式(,则会在参数替换后强制展开。没有理想的方法可以做到这一点。您需要重新编码(或使用外部命令,如realpath
(路径扩展。或者您使用";eval";以强制对您的"$1〃;。但是,如果参数来自用户(可以使用$(rm -fr /)
作为参数,eval会执行它(,这将是一个巨大的安全漏洞,此外,如果文件名包含"$&">
如果你知道这些模式看起来总是像你的例子(可能是波浪号和一些*之类的(,那么你可以自己做波浪号替换,并保持代码的其余部分原样
list ()
{
param=${1/#~/$HOME}
for f in $param; do
echo $f
done
}
list "~/files/list.txt"
不是最好的解决方案。但最接近你的那个。
tl;dr:
- 问题是替换的顺序是bash。您需要通过在执行前的几个阶段重写命令来了解bash是如何工作的
- 更具体地说,因为~在参数和变量之前展开。因此,如果x="0"~/*&";,则
echo $x
表示~expansion后的echo $x
(在echo $x
中为no~(,然后是变量expansion($x被其值替换(后的echo ~/*
,然后是*expansion之后的echo ~/*
(因为您必须将目录按字面名称命名为~,*不匹配任何内容( - 最简单的解决方案是让list接受多个参数,而不仅仅是一个,让扩展发生在对list的调用之前(所以不要将要列出的参数封闭在"中(,然后,通过考虑$1只是许多参数中的第一个来重写list
- 如果你坚持只列出一个论点,你就必须自己处理潜在的问题。与
./dir/*/*.txt
0类似,如果~在模式开始时总是单个~(而不是~用户(