如何在 bash shell 脚本中正确处理通配符扩展?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/258211/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to properly handle wildcard expansion in a bash shell script?
提问by iankits
#!/bin/bash
hello()
{
SRC=
DEST=
for IP in `cat /opt/ankit/configs/machine.configs` ; do
echo $SRC | grep '*' > /dev/null
if test `echo $?` -eq 0 ; then
for STAR in $SRC ; do
echo -en "$IP"
echo -en "\n\t ARG1=$STAR ARG2=\n\n"
done
else
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
fi
done
}
hello
The above is the shell script which I provide source (SRC) & desitnation (DEST) path. It worked fine when I did not put in a SRC path with wild card ''. When I run this shell script and give ''.pdf or '*'as follows:
以上是我提供的源(SRC)和目标(DEST)路径的shell脚本。当我没有使用通配符“ ”放入 SRC 路径时,它工作正常。当我运行这个 shell 脚本并给出 ''.pdf 或 '*' 时,如下所示:
root@ankit1:~/as_prac# ./test.sh /home/dev/Examples/*.pdf /ankit_test/as
I get the following output:
我得到以下输出:
192.168.1.6
ARG1=/home/dev/Examples/case_Contact.pdf ARG2=/home/dev/Examples/case_howard_county_library.pdf
The DEST is /ankit_test/as but DEST also get manupulated due to '*'. The expected answer is
DEST 是 /ankit_test/as 但 DEST 也因“*”而被操纵。预期的答案是
ARG1=/home/dev/Examples/case_Contact.pdf ARG2=/ankit_test/as
So, if you understand what I am trying to do, please help me out to solve this BUG. I'll be grateful to you.
所以,如果你明白我在做什么,请帮我解决这个 BUG。我会感激你的。
Thanks in advance!!!
提前致谢!!!
I need to know exactly how I use '*.pdf' in my program one by one without disturbing DEST.
我需要确切地知道如何在我的程序中一一使用“*.pdf”而不打扰 DEST。
回答by dogbane
Your script needs more work. Even after escaping the wildcard, you won't get your expected answer. You will get:
您的脚本需要更多的工作。即使在转义通配符之后,您也不会得到预期的答案。你会得到:
ARG1=/home/dev/Examples/*.pdf ARG2=/ankit__test/as
Try the following instead:
请尝试以下操作:
for IP in `cat /opt/ankit/configs/machine.configs`
do
for i in $SRC
do
echo -en "$IP"
echo -en "\n\t ARG1=$i ARG2=$DEST\n\n"
done
done
Run it like this:
像这样运行它:
root@ankit1:~/as_prac# ./test.sh "/home/dev/Examples/*.pdf" /ankit__test/as
回答by agnul
The shell will expand wildcards unless you escape them, so for example if you have
除非您转义通配符,否则外壳将展开通配符,例如,如果您有
$ ls
one.pdf two.pdf three.pdf
and run your script as
并运行您的脚本
./test.sh *.pdf /ankit__test/as
it will be the same as
它将与
./test.sh one.pdf two.pdf three.pdf /ankit__test/as
which is not what you expect. Doing
这不是您所期望的。正在做
./test.sh \*.pdf /ankit__test/as
should work.
应该管用。
回答by paxdiablo
If you can, change the order of the parameters passed to your shell script as follows:
如果可以,请更改传递给 shell 脚本的参数的顺序,如下所示:
./test.sh /ankit_test/as /home/dev/Examples/*.pdf
That would make your life a lot easier since the variable part moves to the end of the line. Then, the following script will do what you want:
这将使您的生活更轻松,因为可变部分移动到行尾。然后,以下脚本将执行您想要的操作:
#!/bin/bash
hello()
{
SRC=
DEST=
for IP in `cat /opt/ankit/configs/machine.configs` ; do
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
done
}
arg2=
shift
while [[ "" != "" ]] ; do
hello $arg2
shift
done
回答by godbyk
Here's what I came up with:
这是我想出的:
#!/bin/bash
hello()
{
# DEST will contain the last argument
eval DEST=$$#
while [ != $DEST ]; do
SRC=
for IP in `cat /opt/ankit/configs/machine.configs`; do
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
done
shift || break
done
}
hello $*
Instead of passing only two parameters to the hello() function, we'll pass in all the arguments that the script got.
我们将传入脚本获得的所有参数,而不是仅将两个参数传递给 hello() 函数。
Inside the hello() function, we first assign the final argument to the DEST var. Then we loop through all of the arguments, assigning each one to SRC, and run whatever commands we want using the SRC and DEST arguments. Note that you may want to put quotation marks around $SRC and $DEST in case they contain spaces. We stop looping when SRC is the same as DEST because that means we've hit the final argument (the destination).
在 hello() 函数中,我们首先将最后一个参数分配给 DEST 变量。然后我们遍历所有参数,将每个参数分配给 SRC,并使用 SRC 和 DEST 参数运行我们想要的任何命令。请注意,您可能需要在 $SRC 和 $DEST 周围加上引号,以防它们包含空格。当 SRC 与 DEST 相同时,我们停止循环,因为这意味着我们已经命中了最后一个参数(目的地)。
回答by Alnitak
OK, this appears to do what you want:
好的,这似乎可以满足您的要求:
#!/bin/bash
hello() {
SRC=
DEST=
while read IP ; do
for FILE in $SRC; do
echo -e "$IP"
echo -e "\tARG1=$FILE ARG2=$DEST\n"
done
done < /tmp/machine.configs
}
hello ""
- You still need to escape any wildcard characters when you invoke the script
- The double quotes are necessary when you invoke the
hello
function, otherwise the mere fact of evaluating$1
causes the wildcard to be expanded, but we don't want that to happen until$SRC
is assigned in the function
- 调用脚本时仍然需要转义任何通配符
- 调用
hello
函数时,双引号是必需的,否则仅仅是求值的事实$1
会导致通配符被扩展,但我们不希望这种情况发生,直到$SRC
在函数中赋值
回答by dogbane
You are also missing a final "done" to close your outer for loop.
您还缺少最后一个“完成”来关闭外部 for 循环。
回答by Mike L
For multiple input files using a wildcard such as *.txt, I found this to work perfectly, no escaping required. It should work just like a native bash app like "ls" or "rm." This was not documented just about anywhere so since I spent a better part of 3 days trying to figure it out I decided I should post it for future readers.
对于使用 *.txt 等通配符的多个输入文件,我发现它可以完美运行,无需转义。它应该像“ls”或“rm”这样的本机bash应用程序一样工作。这几乎没有在任何地方记录,所以因为我花了 3 天的大部分时间试图弄清楚它我决定我应该将它发布给未来的读者。
Directory contains the following files (output of ls)
目录包含以下文件(ls的输出)
file1.txt file2.txt file3.txt
Run script like
像这样运行脚本
$ ./script.sh *.txt
Or even like
甚至喜欢
$ ./script.sh file{1..3}.txt
The script
剧本
#!/bin/bash
# store default IFS, we need to temporarily change this
sfi=$IFS
#set IFS to $'\n\' - new line
IFS=$'\n'
if [[ -z $@ ]]
then
echo "Error: Missing required argument"
echo
exit 1
fi
# Put the file glob into an array
file=("$@")
# Now loop through them
for (( i=0 ; i < ${#file[*]} ; i++ ));
do
if [ -w ${file[$i]} ]; then
echo ${file[$i]} " writable"
else
echo ${file[$i]} " NOT writable"
fi
done
# Reset IFS to its default value
IFS=$sfi
The output
输出
file1.txt writable
file2.txt writable
file3.txt writable
The key was switching the IFS (Internal Field Separator) temporarily. You have to be sure to store this before switching and then switch it back when you are done with it as demonstrated above.
关键是暂时切换 IFS(内部字段分隔符)。您必须确保在切换之前存储它,然后在完成后将其切换回,如上所示。
Now you have a list of expanded files (with spaces escaped) in the file[] array which you can then loop through. I like this solution the best, easiest to program for and easiest for the users.
现在您在 file[] 数组中有一个扩展文件列表(带有转义空格),然后您可以循环遍历该列表。我最喜欢这个解决方案,它是最好的、最容易编程的、对用户最简单的。
回答by Alnitak
There's no need to spawn a shell to look at the $?
variable, you can evaluate it directly.
无需生成 shell 来查看$?
变量,您可以直接对其进行评估。
It should just be:
它应该只是:
if [ $? -eq 0 ]; then
回答by dannysauer
You're running
你在跑步
./test.sh /home/dev/Examples/*.pdf /ankit_test/as
and your interactive shell is expanding the wildcard before the script gets it. You just need to quote the first argument when you launch it, as in
并且您的交互式 shell 在脚本获取通配符之前正在扩展通配符。你只需要在启动它时引用第一个参数,如
./test.sh "/home/dev/Examples/*.pdf" /ankit_test/as
and then, in your script, quote "$SRC" anywhere where you literally want the things with wildcards (ie, when you do echo $SRC
, instead use echo "$SRC"
) and leave it unquoted when you want the wildcards expanded. Basically, always put quotes around things which might contain shell metacharacters unless you want the metacharacters interpreted. :)
然后,在您的脚本中,在您真正想要使用通配符的任何地方引用“$SRC”(即,当您这样做时echo $SRC
,而是使用echo "$SRC"
),并在您想要扩展通配符时将其不加引号。基本上,除非您想要解释元字符,否则总是在可能包含 shell 元字符的内容周围加上引号。:)