如何将星号传递给 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 第一个文件。请观看下面的视频以查看实际比较。我不知道为什么会这样。

有人知道怎么解决吗?谢谢。

视频:problem in action


更新:

问题已解决。谢谢大家的回答。现在我的脚本按预期工作。在此处观看视频: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 开始执行以下工作流程:

  1. 解析和词法分析
  2. 扩张
    1. 大括号展开
    2. 扩展
    3. 变量展开
    4. 算术和其他替换
    5. 命令替换
    6. 分词
    7. 文件名生成(通配)
  3. 删除引号

毕竟上面只有bash

  • 会执行一些外部命令,如lsdir.sh...等,
  • 或将对已知关键字和 builtins 执行一些 "internal" 操作,例如 echoforif 等...

如您所见,倒数第二个是文件名生成(通配)。所以,在你的情况下 - 如果 test* 匹配一些文件,你的 bash 扩展 willcard 字符(也就是通配符)。

所以,

  • 当您输入 dir.sh test*,
  • and test* 匹配一些文件
  • bash首先进行扩展
  • 之后将执行命令 dir.sh 已经扩展文件名
  • 例如脚本被执行(在你的情况下)为:dir.sh test.pas test.swift

顺便说一句,它与您的 ls 示例的 same 方式完全一致:

  • bashls 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 所做的那样。 当我需要从脚本中调用带有星号“*”的命令时,这解决了我的问题。