简体   繁体   English

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

[英]how to pass asterisk into ls command inside bash script

Hi… Need a little help here… 嗨...这里需要一点帮助......

I tried to emulate the DOS' dir command in Linux using bash script. 我尝试使用bash脚本在Linux中模拟DOS的dir命令。 Basically it's just a wrapped ls command with some parameters plus summary info. 基本上它只是一个包含ls命令,带有一些参数和摘要信息。 Here's the script: 这是脚本:

#!/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

The problem is when the argument contains an asterisk (*), the ls within the script acts differently compare to the direct ls command given at the prompt. 问题是,当参数包含星号(*)的ls脚本内不同行为比较直接的ls命令在提示发出。 Instead of return the whole files list, the script only returns the first file. 该脚本仅返回第一个文件,而不是返回整个文件列表。 See the video below to see the comparation in action. 请参阅下面的视频,了解实际比较情况。 I don't know why it behaves like that. 我不知道它为什么会这样。

Anyone knows how to fix it? 任何人都知道如何解决它? Thank you. 谢谢。

Video: problem in action 视频: 行动中的问题


UPDATE : 更新

The problem has been solved. 问题已经解决了。 Thank you all for the answers. 谢谢大家的答案。 Now my script works as expected. 现在我的脚本按预期工作了。 See the video here: http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif 观看视频: http//i.giphy.com/3o8dp1YLz4fIyCbOAU.gif

The asterisk * is expanded by the shell when it parses the command line. 在解析命令行时,shell会扩展星号* In other words, your script doesn't get a parameter containing an asterisk, it gets a list of files as arguments. 换句话说,您的脚本没有获得包含星号的参数,它获取文件列表作为参数。 Your script only works with $1 , the first argument. 您的脚本仅适用于$1 ,即第一个参数。 It should work with "$@" instead. 它应该与"$@"使用。

This is because when you retrieve $1 you assume the shell does NOT expand * . 这是因为当你检索$1你认为shell不会扩展*

In fact, when * (or other glob) matches, it is expanded, and broken into segments by $IFS , and then passed as $1 , $2 , etc. 事实上,当* (或其他glob)匹配时,它会被扩展,并被$IFS分成段,然后传递为$1$2等。

You're lucky if you simply retrieved the first file. 如果您只是检索第一个文件,那就太幸运了。 When your first file's path contains spaces, you'll get an error because you only get the first segment before the space. 当您的第一个文件的路径包含空格时,您将收到错误,因为您只获得空格之前的第一个段。

Seriously, read this and especially this . 说真的,读这个 ,特别是这个 Really. 真。


And please don't do things like 请不要做像这样的事情

CMD=whatever you get from user input; $CMD;

You are begging for trouble. 你在乞求麻烦。 Don't execute arbitrary string from the user. 不要从用户执行任意字符串。

Both above answers already answered your question. 以上两个答案都已经回答了你的问题 So, i'm going a bit more verbose. 所以,我会更加冗长。

In your terminal is running the bash interpreter (probably). 在您的终端中运行bash解释器(可能)。 This is the program which parses your input line(s) and doing "things" based on your input. 这是一个程序,它根据您的输入解析您的输入行和做“事情”。

When you enter some line the bash start doing the following workflow: 当你输入一些行时, bash开始执行以下工作流程:

  1. parsing and lexical analysis 解析和词法分析
  2. expansion 扩张
    1. brace expansion 支撑扩张
    2. tidle expansion 收缩扩张
    3. variable expansion 变量扩展
    4. artithmetic and other substitutions 算术和其他替代
    5. command substitution 命令替换
    6. word splitting 分词
    7. filename generation (globbing) 文件名生成(通配)
  3. removing quotes 删除引号

Only after all above the bash 毕竟只有在bash上面

  • will execute some external commands, like ls or dir.sh ... etc., 将执行一些外部命令,如lsdir.sh ......等,
  • or will do so some "internal" actions for the known keywords and builtins like echo , for , if etc... 或者将对已知关键字和builtins执行一些“内部”操作,如echoforif等...

As you can see, the second last is the filename generation (globbing) . 如您所见,倒数第二个是文件名生成(globbing) So, in your case - if the test* matches some files, your bash expands the willcard characters (aka does the globbing). 所以,在你的情况下 - 如果test*匹配某些文件,你的bash 会扩展 willcard字符(也就是全局)。

So, 所以,

  • when you enter dir.sh test* , 当你输入dir.sh test*
  • and the test* matches some files 并且 test*匹配一些文件
  • the bash does the expansion first bash 首先进行扩展
  • and after will execute the command dir.sh with already expanded filenames 然后将执行带有已扩展文件名的命令dir.sh
  • eg the script get executed (in your case) as: dir.sh test.pas test.swift 例如,脚本执行(在您的情况下)为: dir.sh test.pas test.swift

BTW, it acts exactly with the same way for your ls example: 顺便说一句,它为您以同样的方式正好充当ls例如:

  • the bash expands the ls test* to ls test.pas test.swift bashls test*扩展为ls test.pas test.swift
  • then executes the ls with the above two arguments 然后用上面两个参数执行ls
  • and the ls will print the result for the got two arguments. 并且ls将打印获得两个参数的结果。
  • with other words, the ls don't even see the test* argument - if it is possible - the bash expands the wilcard characters. 换句话说, ls甚至没有看到test*参数 - 如果可能的话 - bash扩展了wilcard字符。 ( * and ? ). *? )。

Now back to your script: add after the shebang the following line: 现在回到你的脚本:在shebang之后添加以下行:

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

and you will immediatelly see, the real argumemts how your script got executed. 并且你会立即看到,真正的争论是你的脚本如何被执行。

also, in such cases is a good practice trying to execute the script in debug-mode, eg 此外,在这种情况下尝试以调试模式执行脚本是一种很好的做法,例如

bash -x dir.sh test*

and you will see, what the script does exactly. 你会看到,剧本的确是什么。

Also, you can do the same for your current interpreter, eg just enter into the terminal 此外,您可以为当前的解释器执行相同的操作,例如,只需进入终端即可

set -x

and try run the dir.sh test* = and you will see, how the bash will execute the dir.sh command. 并尝试运行dir.sh test* =你会看到bash将如何执行dir.sh命令。 (to stop the debug mode, just enter set +x ) (要停止调试模式,只需输入set +x

Everbody is giving you valuable advice which you should definitely should follow! Everbody正在为您提供宝贵的建议,您应该遵循这些建议!

But here is the real answer to your question. 但这是你问题的真正答案。

To pass unexpanded arguments to any executable you need to single quote them: 要将未展开的参数传递给任何可执行文件,您需要单引号:

./your_script '*'

The best solution I have is to use the eval command, in this way: 我有的最好的解决方案是使用eval命令,这样:

#!/bin/bash

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

The eval command takes its arguments and evaluates them into the command as the shell does. eval命令获取其参数并将其评估为shell中的命令。 This solves my problem when I need to call a command with asterisk '*' in it from a script. 当我需要从脚本中调用带有星号'*'的命令时,这解决了我的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM