如何将星号传递给bash脚本中的ls命令

时间:2015-12-25 09:11:23

标签: linux bash ls

嗨......这里需要一点帮助......

我尝试使用bash脚本在Linux中模拟DOS'dir命令。基本上它只是一个包含ls命令,带有一些参数和摘要信息。这是脚本:

#!/bin/bash

# default to current folder 
if [ -z "$1" ]; then var=.;
  else var="$1"; 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 += $3 }; 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命令的行为不同。该脚本仅返回第一个文件,而不是返回整个文件列表。请参阅下面的视频,了解实际比较情况。我不知道为什么会这样。

任何人都知道如何修复它?谢谢。

视频:problem in action

更新

问题已经解决了。谢谢大家的答案。现在我的脚本按预期工作了。观看视频:http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif

5 个答案:

答案 0 :(得分:5)

在解析命令行时,shell会扩展星号*。换句话说,您的脚本没有获得包含星号的参数,它获取文件列表作为参数。您的脚本仅适用于第一个参数$1。它应该与"$@"匹配。

答案 1 :(得分:4)

这是因为当您检索$1时,您认为shell不会展开*

事实上,当*(或其他全局)匹配时,它会被展开,并按$IFS分成若干段,然后传递为$1$2,等

如果您只是检索了第一个文件,那么您很幸运。当您的第一个文件的路径包含空格时,您将收到错误,因为您只获得该空格之前的第一个段。

说真的,请阅读this,尤其是this。真。

请不要做

之类的事情
CMD=whatever you get from user input; $CMD;
你正在乞求麻烦。不要从用户那里执行任意字符串。

答案 2 :(得分:1)

以上两个答案都已经回答了你的问题。所以,我会更加冗长。

在您的终端中运行bash解释器(可能)。这是一个程序,它根据您的输入解析您的输入行和做“事情”。

当您输入某行时,bash开始执行以下工作流程:

  1. 解析和词法分析
  2. 扩张
    1. 支撑扩张
    2. tidle expansion
    3. 变量扩展
    4. 算术和其他替代
    5. 命令替换
    6. 分词
    7. 文件名生成(globbing)
  3. 删除引号
  4. 仅在bash

    之后
    • 将执行一些外部命令,例如lsdir.sh ...等等,
    • 或将针对已知关键字进行一些“内部”操作,builtinsechofor等等{...}}

    如您所见,最后一个是文件名生成(通配)。因此,在您的情况下 - 如果if与某些文件匹配,那么您的test* 会扩展 willcard字符(也就是通配符)。

    所以,

    • 当您输入bash时,
    • dir.sh test*匹配某些文件
    • bash执行扩展第一次
    • 之后将执行已扩展文件名的命令test*
    • e.g。脚本执行(在您的情况下)为:dir.sh

    顺便说一句,它完全符合dir.sh test.pas test.swift示例的相同方式:

    • lsbash扩展为ls test*
    • 然后使用上述两个参数执行ls test.pas test.swift
    • 并且ls将打印获得两个参数的结果。
    • 换句话说,ls甚至看不到ls参数 - 如果可能的话 - test*扩展wilcard字符。 (bash*)。

    现在回到你的脚本:在shebang之后添加以下行:

    ?

    你会立刻看到,真正的争论是你的剧本如何被执行。

    同样,在这种情况下尝试以调试模式执行脚本是一种很好的做法,例如

    echo "the $0 got this arguments: $@"
    

    你会看到,剧本的确切行为。

    此外,您可以为当前的口译员执行相同操作,例如:进入终端

    bash -x dir.sh test*
    

    并尝试运行set -x =,您将看到dir.sh test*将如何执行bash命令。 (要停止调试模式,只需输入dir.sh

答案 3 :(得分:0)

Everbody正在为您提供宝贵的建议,您应该遵循这些建议!

但这是你问题的真实答案。

要将未展开的参数传递给任何可执行文件,您需要单引号:

./your_script '*'

答案 4 :(得分:0)

我最好的解决方案是通过以下方式使用eval命令:

#!/bin/bash

cmd="some command \"with_quetes_and_asterisk_in_it*\""
echo "$cmd"
eval $cmd

eval命令采用其参数,并像shell一样将其求值到命令中。 当我需要从脚本中调用带有星号“ *”的命令时,这解决了我的问题。