Using multiple layers of quotes in bash - linux

I'm trying to write a bash script, and I'm running into a quoting problem.
The end result I'm after is for my script to call:
lwp-request -U -e -H "Range: bytes=20-30"
My script file looks like:
CLIENT=lwp-request
REQ_HDRS=-U
RSP_HDRS=-e
RANGE="-H "Range: bytes=20-30"" # Obviously can't do nested quotes here
${CLIENT} ${REQ_HDRS} ${RSP_HDRS} ${RANGE}
I know I can't use nested-quotes. But how can I accomplish this?

Normally, you could escape the inner quotes with \:
RANGE="-H \"Range: bytes=20-30\""
But this won't work when running a command – unless you put eval before the whole thing:
RANGE="-H \"Range: bytes=20-30\""
eval $CLIENT $REQ_HDRS $RSP_HDRS $RANGE
However, since you're using bash, not sh, you can put separate arguments in arrays:
RANGE=(-H "Range: bytes=20-30")
$CLIENT $REQ_HDRS $RSP_HDRS "${RANGE[#]}"
This can be extended to:
ARGS=(
-U # Request headers
-e # Response headers
-H "Range: bytes=20-30" # Range
)
$CLIENT "${ARGS[#]}"

try this:
RANGE='\"-H \"Range: bytes=20-30\"\"
you can espape using '' and \"
no_error=''errors=\"0\"'';

You can use the fact that both '' and "" can be used for strings.
So you can do things like this:
x='Say "hi"'
y="What's up?"

Related

Passing an argument with single and double quotes to shell script

We are trying to pass an argument with single and double quotes to a shell script and executing with this argument. In echo its printing correctly but for the command we are getting as "Unterminated quoted value"
Please see the script and argument passing method:
[root#geopc]/root# ./myscript.sh 'localhost:9199/jmxrmi -O java.lang:type=GarbageCollector,name="PS MarkSweep" -A CollectionCount -K duration'
#!/bin/bash
out=`/usr/lib64/nagios/plugins/check_jmx -U service:jmx:rmi:///jndi/rmi://$1`
echo $1
echo $out
After executing we are getting output as
$1 : localhost:9199/jmxrmi -O java.lang:type=GarbageCollector,name="PS MarkSweep" -A CollectionCount -K duration
$out : JMX CRITICAL Unterminated quoted value
In Shell Script we we hard code value of $1 and then executing we are getting correct result.
We tried passing of arguments as follows:
./myscript.sh 'localhost:9199/jmxrmi -O java.lang:type=GarbageCollector,name=\"PS MarkSweep\" -A CollectionCount -K duration'
in this case error is : JMX CRITICAL Invalid character '"' in value part of property
./myscript.sh 'localhost:9199/jmxrmi -O java.lang:type=GarbageCollector,name="\"PS MarkSweep\"" -A CollectionCount -K duration'
in this case error is JMX CRITICAL Unterminated quoted value
So anyone please help me on it.
The problem is that you're expecting quotes and escapes in variables to be interpreted the same as quotes in your script.
In Java terms, this is the same as expecting:
String input="\"foo\", \"bar\"";
String[] parameters = { input };
to be the same as this:
String[] parameters = { "foo", "bar" };
The real solution in both language is to let the input be an array of elements:
#!/bin/bash
out=`/usr/lib64/nagios/plugins/check_jmx -U service:jmx:rmi:///jndi/rmi://"$1" "${#:2}"`
echo "$1"
echo "$out"
and then running:
./myscript.sh localhost:9199/jmxrmi -O java.lang:type=GarbageCollector,name="PS MarkSweep" -A CollectionCount -K duration
HOWEVER!!
This requires discipline and understanding.
You'll notice that this is not a drop-in solution: the script is now being called with multiple parameters. This is a very strict requirement. If this script is being called by another program, that program also has to be aware of this. It needs to be handled end-to-end.
If you store this in a config file for example, it'll be YOUR responsibility to serialize the string in such a way that YOU can deserialized into a list. You can't just hope that it'll work out, because it won't. Unlike Java, there will be no compiler warning when you confuse strings and lists.
If this is not something you want to get involved with, you can instead use eval. This is a bad practice because of security and robustness issues, but sometimes it's Good Enough:
yourcommand="/usr/lib64/nagios/plugins/check_jmx -U service:jmx:rmi:///jndi/rmi://$1"
echo "$yourcommand" # Make sure this writes the right command
eval "$yourcommand" # This will execute it as if you copy-pasted it

Retrieve URL components using bash

I have a massive list of URLs in a text file, which I'd like to download using wget. This seems simple enough:
#!/bin/bash
cat list.txt | \
while read CMD; do
wget $CMD; done;
However, wget uses the basename of the file as the download location, which results in renaming schemes, such as file.txt.1, file.txt.2 and so on.
An $URL can look like this:
http://sub.domain.com/some/folder/to/file.txt
Where http://sub.domain.com/some/ is always the same. Now, in JS I would do $URL.split("http://sub.domain.com/some/")[1], but this doesn't quite seem to work in Bash:
IFS="http://sub.domain.com/some/" read -a url <<< "http://sub.domain.com/some/folder/to/file.txt"
echo "${url[1]}"; // always empty.
Use the shell's parameter expansion operator to remove the prefix:
base=${CMD#http://sub.domain.com/some/}
BTW, you should get out of the habit of using all-uppercase variable names in shell scripts. These are conventionally used for environment variables.
If the length of the prefix is static you could do the following:
#!/bin/bash
while read line
do
suffix=${line:${#line} - LENGTH}
wget $line -O $suffix
done < "list.txt"

Why does bash insert additional quotes

I need to pipe an expression including single quotes to a command, but bash inserts loads of extra quotes which breaks my command. As a really simple example take:
#!/bin/bash -x
echo 'EXPRESSION' | more
which gives:
+ echo EXPRESSION
+ more
EXPRESSION
As I want the single quotes to be displayed, I must escape them:
#!/bin/bash -x
echo \'EXPRESSION\' | more
Which now gives me:
+ echo ''\''EXPRESSION'\'''
+ more
'EXPRESSION'
So within the script, I get this bizarre ''\''EXPRESSION'\''' thing. The command I am piping the expression to is an executable that interacts with a document management system, and expects a specific format—which includes single quotes around EXPRESSION and not ''\'' and '\'''.
Is there any way to stop bash from adding the additional quotes and backslashes? I've messed around with strings and eval etc., but have failed to get rid of those additional quotes.
You can also try it with double quotes like this,
echo "'EXPRESSION'"|more
Output will be,
'EXPRESSION'
The /bin/bash -x is producing the top 2 lines. Your code produces the 3rd line. If you want you can just remove the -x and you should see it in a better way.
The above answer from Skynet works just fine, but with the -x option, it still shows 3 lines. It's just what the -x does.

How to pass local shell script variable to expect?

My question is related to How to pass variables from a shell script to an expect script?
but its slightly different:
Apart from passing two runtime shell script variables, I want to pass a variable thats inside the shell script For eg:
#!/bin/sh
d=`date '+%Y%m%d_%H%M'`
expect -c '
expect "sftp>"
#use $d here(how?)
'
You don't need to pass a date variable into expect. It has a very good date command builtin:
expect -c '
# ...
set d [timestamp -format "%Y%m%d_%H%M"]
something_with $d
# ...
'
If you need to do more complicated date manipulation, read about the Tcl clock command
Another good technique to pass shell variables to expect (without having to do complicated/messy quoting/escaping) is to use the environment: export your shell variables, and then access them with expect's global env array:
export d=$(date ...)
expect -c 'puts "the date is $env(d)"'
This seems the wrong way to do things. You should set up SSH keys (with ssh-keygen and ssh-copy-id), google about this.
Anyway, try this :
#!/bin/sh
d=`date '+%Y%m%d_%H%M'`
expect -c "
something_with $d"
Note the double quotes instead of single quotes.
"Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[#]}", "a & b". Use 'single quotes' for code or literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. See http://mywiki.wooledge.org/Quotes , http://mywiki.wooledge.org/Arguments and http://wiki.bash-hackers.org/syntax/words

How connect variable with a string in Bash?

I am writing a bash script , I used 1 variable in my bash file like below
list=`/home/ea/students'
I wrote below link in my bash but I got error
cat $list /admin.txt
Do you know how can I connect variable and string together?
Firstly you need to use single quotes (''') around strings, not backticks ('`')
list='/home/ea/students'
To append a string to a variable, do the following:
list=${list}/admin.txt
Demo:
echo $list
/home/ea/students/admin.txt
Try this:
list='/home/ea/students'
...
cat "${list}/admin.txt"
You can go with either:
cat "$list/admin.txt"
In this case the braces '{}' are not mandatory
as the / is not a valid identifier name character.
... or, if you need a variable, recent bash versions provide more concise way for appending:
bash-4.1$ list=/home/ea/students
bash-4.1$ list+=/admin.txt
bash-4.1$ printf '%s\n' "$list"
/home/ea/students/admin.txt

Resources