如何将星号传递给 bash 脚本中的 ls 命令
how to pass asterisk into ls command inside bash script
嗨……这里需要一点帮助……
我尝试使用 bash 脚本在 Linux 中模拟 DOS 的 dir
命令。基本上它只是一个带有一些参数和摘要信息的包装 ls
命令。这是脚本:
#!/bin/bash
# default to current folder
if [ -z "" ]; then var=.;
else var=""; fi
# check file existence
if [ -a "$var" ]; then
# list contents with color, folder first
CMD="ls -lgG $var --color --group-directories-first"; $CMD;
# sum all files size
size=$(ls -lgGp "$var" | grep -v / | awk '{ sum += }; END { print sum }')
if [ "$size" == "" ]; then size="0"; fi
# create summary
if [ -d "$var" ]; then
folder=$(find $var/* -maxdepth 0 -type d | wc -l)
file=$(find $var/* -maxdepth 0 -type f | wc -l)
echo "Found: $folder folders "
echo " $file files $size bytes"
fi
# error message
else
echo "dir: Error \"$var\": No such file or directory"
fi
问题是当参数包含星号 (*) 时,脚本中的 ls
与提示符下直接给出的 ls
命令的行为不同。而不是 return 整个文件列表,脚本只 return 第一个文件。请观看下面的视频以查看实际比较。我不知道为什么会这样。
有人知道怎么解决吗?谢谢。
更新:
问题已解决。谢谢大家的回答。现在我的脚本按预期工作。在此处观看视频:http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif
星号 *
在解析命令行时被 shell 扩展。换句话说,您的脚本不会获得包含星号的参数,它会获得文件列表作为参数。您的脚本仅适用于第一个参数 </code>。它应该与 <code>"$@"
一起使用。
这是因为当您检索 </code> 时,您假设 shell 不会扩展 <code>*
。
实际上,当*
(或其他glob)匹配时,它被扩展,并被$IFS
分成段,然后作为</code>、[=16=传递],等等</p>
<p>如果您只是检索到第一个文件,您就很幸运了。当您的第一个文件的路径包含 spaces 时,您将收到一个错误,因为您只得到 space.</p> 之前的第一段
<p>认真阅读 <a href="http://mywiki.wooledge.org/BashPitfalls" rel="noreferrer">this</a> and especially <a href="http://mywiki.wooledge.org/WordSplitting" rel="noreferrer">this</a>。真的。</p>
<hr>
<p>请不要做</p>
<pre><code>CMD=whatever you get from user input; $CMD;
你这是在找麻烦。不要执行用户的任意字符串。
以上两个答案已经回答了你的问题。所以,我要详细一点。
在您的终端中 运行正在使用 bash
解释器(可能)。这是解析您的输入行并根据您的输入执行 "things" 的程序。
当您输入某行时,bash
开始执行以下工作流程:
- 解析和词法分析
- 扩张
- 大括号展开
- 扩展
- 变量展开
- 算术和其他替换
- 命令替换
- 分词
- 文件名生成(通配)
- 删除引号
毕竟上面只有bash
- 会执行一些外部命令,如
ls
或dir.sh
...等,
- 或将对已知关键字和
builtins
执行一些 "internal" 操作,例如 echo
、for
、if
等...
如您所见,倒数第二个是文件名生成(通配)。所以,在你的情况下 - 如果 test*
匹配一些文件,你的 bash
扩展 willcard 字符(也就是通配符)。
所以,
- 当您输入
dir.sh test*
,
- and
test*
匹配一些文件
- bash首先进行扩展
- 之后将执行命令
dir.sh
已经扩展文件名
- 例如脚本被执行(在你的情况下)为:
dir.sh test.pas test.swift
顺便说一句,它与您的 ls
示例的 same 方式完全一致:
bash
将 ls test*
扩展为 ls test.pas test.swift
- 然后用上面两个参数
执行ls
- 和
ls
将打印得到的 两个 参数的结果。
- 换句话说,
ls
甚至看不到 test*
参数——如果可能的话——bash
扩展了通配符。 (*
和 ?
)。
现在回到您的脚本:在 shebang 之后添加以下行:
echo "the [=10=] got this arguments: $@"
您会立即看到,真正的参数是您的脚本是如何执行的。
此外,在这种情况下尝试在调试模式下执行脚本是一个很好的做法,例如
bash -x dir.sh test*
您将看到脚本的具体作用。
另外,您也可以为您当前的口译员做同样的事情,例如只需进入终端
set -x
并尝试 运行 和 dir.sh test*
=,您将看到 bash
将如何执行 dir.sh
命令。 (要停止调试模式,只需输入 set +x
)
每个人都在给你宝贵的建议,你绝对应该遵循!
但这里是您问题的真正答案。
要将未扩展的参数传递给任何可执行文件,您需要将它们用单引号引起来:
./your_script '*'
我的最佳解决方案是使用 eval 命令,方法如下:
#!/bin/bash
cmd="some command \"with_quetes_and_asterisk_in_it*\""
echo "$cmd"
eval $cmd
eval 命令接受它的参数并将它们计算到命令中,就像 shell 所做的那样。
当我需要从脚本中调用带有星号“*”的命令时,这解决了我的问题。
嗨……这里需要一点帮助……
我尝试使用 bash 脚本在 Linux 中模拟 DOS 的 dir
命令。基本上它只是一个带有一些参数和摘要信息的包装 ls
命令。这是脚本:
#!/bin/bash
# default to current folder
if [ -z "" ]; then var=.;
else var=""; fi
# check file existence
if [ -a "$var" ]; then
# list contents with color, folder first
CMD="ls -lgG $var --color --group-directories-first"; $CMD;
# sum all files size
size=$(ls -lgGp "$var" | grep -v / | awk '{ sum += }; END { print sum }')
if [ "$size" == "" ]; then size="0"; fi
# create summary
if [ -d "$var" ]; then
folder=$(find $var/* -maxdepth 0 -type d | wc -l)
file=$(find $var/* -maxdepth 0 -type f | wc -l)
echo "Found: $folder folders "
echo " $file files $size bytes"
fi
# error message
else
echo "dir: Error \"$var\": No such file or directory"
fi
问题是当参数包含星号 (*) 时,脚本中的 ls
与提示符下直接给出的 ls
命令的行为不同。而不是 return 整个文件列表,脚本只 return 第一个文件。请观看下面的视频以查看实际比较。我不知道为什么会这样。
有人知道怎么解决吗?谢谢。
更新:
问题已解决。谢谢大家的回答。现在我的脚本按预期工作。在此处观看视频:http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif
星号 *
在解析命令行时被 shell 扩展。换句话说,您的脚本不会获得包含星号的参数,它会获得文件列表作为参数。您的脚本仅适用于第一个参数 </code>。它应该与 <code>"$@"
一起使用。
这是因为当您检索 </code> 时,您假设 shell 不会扩展 <code>*
。
实际上,当*
(或其他glob)匹配时,它被扩展,并被$IFS
分成段,然后作为</code>、[=16=传递],等等</p>
<p>如果您只是检索到第一个文件,您就很幸运了。当您的第一个文件的路径包含 spaces 时,您将收到一个错误,因为您只得到 space.</p> 之前的第一段
<p>认真阅读 <a href="http://mywiki.wooledge.org/BashPitfalls" rel="noreferrer">this</a> and especially <a href="http://mywiki.wooledge.org/WordSplitting" rel="noreferrer">this</a>。真的。</p>
<hr>
<p>请不要做</p>
<pre><code>CMD=whatever you get from user input; $CMD;
你这是在找麻烦。不要执行用户的任意字符串。
以上两个答案已经回答了你的问题。所以,我要详细一点。
在您的终端中 运行正在使用 bash
解释器(可能)。这是解析您的输入行并根据您的输入执行 "things" 的程序。
当您输入某行时,bash
开始执行以下工作流程:
- 解析和词法分析
- 扩张
- 大括号展开
- 扩展
- 变量展开
- 算术和其他替换
- 命令替换
- 分词
- 文件名生成(通配)
- 删除引号
毕竟上面只有bash
- 会执行一些外部命令,如
ls
或dir.sh
...等, - 或将对已知关键字和
builtins
执行一些 "internal" 操作,例如echo
、for
、if
等...
如您所见,倒数第二个是文件名生成(通配)。所以,在你的情况下 - 如果 test*
匹配一些文件,你的 bash
扩展 willcard 字符(也就是通配符)。
所以,
- 当您输入
dir.sh test*
, - and
test*
匹配一些文件 - bash首先进行扩展
- 之后将执行命令
dir.sh
已经扩展文件名 - 例如脚本被执行(在你的情况下)为:
dir.sh test.pas test.swift
顺便说一句,它与您的 ls
示例的 same 方式完全一致:
bash
将ls test*
扩展为ls test.pas test.swift
- 然后用上面两个参数 执行
- 和
ls
将打印得到的 两个 参数的结果。 - 换句话说,
ls
甚至看不到test*
参数——如果可能的话——bash
扩展了通配符。 (*
和?
)。
ls
现在回到您的脚本:在 shebang 之后添加以下行:
echo "the [=10=] got this arguments: $@"
您会立即看到,真正的参数是您的脚本是如何执行的。
此外,在这种情况下尝试在调试模式下执行脚本是一个很好的做法,例如
bash -x dir.sh test*
您将看到脚本的具体作用。
另外,您也可以为您当前的口译员做同样的事情,例如只需进入终端
set -x
并尝试 运行 和 dir.sh test*
=,您将看到 bash
将如何执行 dir.sh
命令。 (要停止调试模式,只需输入 set +x
)
每个人都在给你宝贵的建议,你绝对应该遵循!
但这里是您问题的真正答案。
要将未扩展的参数传递给任何可执行文件,您需要将它们用单引号引起来:
./your_script '*'
我的最佳解决方案是使用 eval 命令,方法如下:
#!/bin/bash
cmd="some command \"with_quetes_and_asterisk_in_it*\""
echo "$cmd"
eval $cmd
eval 命令接受它的参数并将它们计算到命令中,就像 shell 所做的那样。 当我需要从脚本中调用带有星号“*”的命令时,这解决了我的问题。