Quote argument strings Shell - linux

Can I quote arguments that have spaces or special chars in the command line? If not, how is this handled. We have one argument that is a multiword string. I'm sure this has been asked many times, but I cant find it.
./myscript name=bob occupation=guy who does stuff

You can use quotes. Something like:
$ var='abc def'
$ echo "$var"
abc def
You can read more here.
Your case example (note I'm calling the script that needs the multiword string from the another script in this example):
sh ./test.sh 'this is a test'
test.sh contains echo $1 and prints this is a test. Hope this solution works.

Related

Execute transliterated string in Bash

I want to execute a bash script from a string, say "echo TEST!" for example.
So I could go like this:
eval "echo TEST!"
Problem is, the string is transliterated in UTF-16LE to shorten the char count for golf purpose (it doesn't shorten the byte count but it's not a problem in the given context), so my actual string is "捥潨吠卅⅔".
I know how to transliterate it back, for example if I do this:
echo 捥潨吠卅⅔|iconv -tUCS2
it prints:
echo TEST!
But when I want to execute from the string, it doesn't work.
I tried this for example:
eval 捥潨吠卅⅔|iconv -tUCS2
but it fails miserably.
Could someone help me?
Note: I usually never use Bash so sorry if the question is really dumb.
You can use command expansion to capture the string for eval:
eval "$(echo 捥潨吠卅⅔|iconv -tUCS2)"
If you are golfing, you can further shorten it with backtics, here strings, and if the resulting command does not contain shell syntax, you can skip eval:
`iconv -tUCS2<<<捥潨吠卅⅔`
It fails because eval expects a commnd or an expression to execute, instead you have to use echo -e in order to pipe text to iconv.

bash echo environment variable containing escaped characters

I have an script that echo the input given, into a file as follows:
echo $# > file.txt
When I pass a sting like "\"" I want it to exactly print "\"" to the file however it prints ".
My question is how can I print all characters of a variable containing a string without considering escapes?
When I use echo in bash like echo "\"" it only prints " while when I use echo '"\""' it prints it correctly. I thought maybe that would be the solution to use single quotes around the variable, however I cannot get the value of a variable inside single quotes.
First, note that
echo $# > file.txt
can fail in several ways. Shellcheck identifies one problem (missing quotes on $#). See the accepted, and excellent, answer to Why is printf better than echo? for others.
Second, as others have pointed out, there is no practical way for a Bash program to know exactly how parameters were specified on the command line. For instance, for all of these invocations
prog \"
prog "\""
prog '"'
the code in prog will see a $1 value that consists of one double-quote character. Any quoting characters that are used in the invocation of prog are removed by the quote removal part of the shell expansions done by the parent shell process.
Normally that doesn't matter. If variables or parameters contain values that would need to be quoted when entered as literals (e.g. "\"") they can be used safely, including passing them as parameters to other programs, by quoting uses of the variable or parameter (e.g. "$1", "$#", "$x").
There is a problem with variables or parameters that require quoting when entered literally if you need to write them in a way that they can be reused as shell input (e.g. by using eval or source/.). Bash supports the %q format specification to the printf builtin to handle this situation. It's not clear what the OP is trying to do, but one possible solution to the question is:
if (( $# > 0 )) ; then
printf -v quoted_params '%q ' "$#" # Add all parameters to 'quoted_params'
printf '%s\n' "${quoted_params% }" # Remove trailing space when printing
fi >file.txt
That creates an empty 'file.txt' when no positional parameters are provided. The code would need to be changed if that is not what is required.
If you run echo \", the function of the backslash in bash is to escape the character after it. This actually enables you to use the double quotes as an argument. You cannot use a backslash by itself; if you want to have a backslash as an argument you need to use another slash to escape that: echo \\
Now if you want to create a string where these things are not escaped, use single quotes: echo '\'
See for a better explanation this post: Difference between single and double quotes in Bash

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. 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
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.
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.
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:
parsing and lexical analysis
expansion
brace expansion
tidle expansion
variable expansion
artithmetic and other substitutions
command substitution
word splitting
filename generation (globbing)
removing quotes
Only after all above the bash
will execute some external commands, like ls or dir.sh... etc.,
or will do so some "internal" actions for the known keywords and 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,
when you enter dir.sh test*,
and the test* matches some files
the bash does the expansion first
and after will execute the command dir.sh with already expanded filenames
e.g. the script get executed (in your case) as: dir.sh test.pas test.swift
BTW, it acts exactly with the same way for your ls example:
the bash expands the ls test* to ls test.pas test.swift
then executes the ls with the above two arguments
and the ls will print the result for the got two arguments.
with other words, the 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)
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 '*'
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.

Pass quoted arguments to shell script and maintain quoting [duplicate]

This question already has answers here:
How do I pass on script arguments that contain quotes/spaces?
(2 answers)
Closed 2 years ago.
I'm attempting to re-use parameters sent to my script as parameters for a command I execute within my script. See example below where I execute mailx.
bash
$./myscript.sh "My quoted Argument"
myscript.sh
mailx -s $1
This ends up being executed as: mailx -s My Quoted Argument.
I tried "$1", but my quotes are thrown away. (Incorrect statement, read answer below)
I tried ""$1"" but my quotes are thrown away.
I tried to do '$1' but that's strong quoting so $1 never gets interpreted.
I realize I can do $#, but that gives me every param.
.... you get the picture
Any help would be appreciated!
mailx -s "$1" correctly passes the value of $1 to mailx as-is, embedded spaces and all.
In the case at hand, this means that My Quoted Argument is passed as a single, literal argument to mailx, which is probably your intent.
In a shell command line, quotes around string literals are syntactic elements demarcating argument boundaries that are removed by the shell in the process of parsing the command line (a process called quote removal).
If you really wanted to pass embedded double-quotes (which would be unusual), you have 2 options:
either: embed the quotes on invocation ./myscript.sh "\"My quoted Argument\""
or: embed the quotes inside myscript.sh: mailx -s "\"$1\""
You may just put escaped quotes around $1 in your script
mailx -s \"$1\"
Can you try passing the argument like this
$./myscript.sh \"My quoted Argument\"?

Passing ( cd / && ls ) command as variable to a bash script

This is my bash script named ooo.
#!/bin/bash
$1
This command works great.
./ooo 'echo test'
The output is:
test
This command fails.
./ooo 'cd / && ls'
There is no output.
Why is there no output?
Try this:
#!/bin/bash
eval "$1"
If your question is "how do I do what I expect to happen here?", then #ooga's answer is perfect.
If your question is "why doesn't this do what I expect?", then the answer is slightly more subtle.
What I think is happening (and the bash rules are intricate, so I'm not 100% positive here) is that
The line $1 is parsed as a one-word 'simple command' (see the bash manpage) – the one word is just $1, and the && in the expansion isn't visible (and in particular doesn't do what you expected), because the parameter hasn't been expanded yet.
That simple command is then split into words. Of course, there's only one 'word' here, so this step is trivial.
The parameter is then expanded, to give the word "cd / && ls"
Then "Word Splitting: The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting." (bash manpage)
Then this word is split into four words, "cd", "/", "&&" and "ls"
Then "COMMAND EXECUTION:
After a command has been split into words, if it results in a simple command and an optional list of arguments, the following actions are taken.[...]"
So the command cd is run with the three following words as arguments. That means that, as William Pursell noted in his comment to your question, cd is given three arguments, two of which it ignores.
If you run your script with
./ooo 'echo / && ls'
you'll see this happening, and see the apparently magic && being passed to the echo command as just a normal string.

Resources