Reputation: 203
Hi… Need a little help here…
I tried to emulate the DOS' dir
command in Linux using bash script. Basically it's just a wrapped ls
command with some parameters plus summary info. 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. 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
Upvotes: 3
Views: 9277
Reputation: 2528
The best solution I have is to use the eval command, in this way:
#!/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. This solves my problem when I need to call a command with asterisk '*' in it from a script.
Upvotes: 0
Reputation: 15175
Everbody is giving you valuable advice which you should definitely should follow!
But here is the real answer to your question.
To pass unexpanded arguments to any executable you need to single quote them:
./your_script '*'
Upvotes: 1
Reputation: 63922
Both above answers already answered your question. So, i'm going a bit more verbose.
In your terminal is running the bash
interpreter (probably). 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:
Only after all above the bash
ls
or dir.sh
... etc.,builtins
like echo
, for
, if
etc...As you can see, the second last is the filename generation (globbing). So, in your case - if the test*
matches some files, your bash
expands the willcard characters (aka does the globbing).
So,
dir.sh test*
,test*
matches some filesdir.sh
with already expanded filenamesdir.sh test.pas test.swift
BTW, it acts exactly with the same way for your ls
example:
bash
expands the ls test*
to ls test.pas test.swift
ls
with the above two argumentsls
will print the result for the got two arguments.ls
don't even see the test*
argument - if it is possible - the bash
expands the wilcard characters. (*
and ?
).Now back to your script: add after the shebang the following line:
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, e.g.
bash -x dir.sh test*
and you will see, what the script does exactly.
Also, you can do the same for your current interpreter, e.g. 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. (to stop the debug mode, just enter set +x
)
Upvotes: 3
Reputation: 11302
This is because when you retrieve $1
you assume the shell does NOT expand *
.
In fact, when *
(or other glob) matches, it is expanded, and broken into segments by $IFS
, and then passed as $1
, $2
, etc.
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.
Upvotes: 5
Reputation: 241908
The asterisk *
is expanded by the shell when it parses the command line. 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. It should work with "$@"
instead.
Upvotes: 7