bash prevent escaping with echo - linux

I am relatively new to linux and am trying to create a TikZ figure parsing a file. In order to do so I read in the file with a $%&-bash script containing the following statement
echo "\fill[color=blue] ($xp,$zp) circle (5pt);" >> $fout
this results in the following output
^Lill[color=blue] ($xp,$zp) circle (5pt);
Obviously echo escapes the \f and I did not find a way around it.
I have tried all options like "-e" "-n" and what have you, have tried all kinds of combinations of " ' etc, but to no avail.
I am stuck as so often with linux, but this time even google didn't help (OMG=Oh My Google!!!!!!!!).

echo should not do backslash escapes by default, unless -e is specified. You can try echo -E to force turning them off (in case you have aliased echo to echo -e or something).
Alternatively, try using single quotes (although now that I think about it, I don't see how it would help):
echo '\fill[color=blue] ('"$xp,$zp"') circle (5pt);' >> $fout

Related

Bash write into file that is named by a variable

I am working on creating a simple script to make it easy to set up a virtual host on Apache server. Currently I don't seem to be able to write the config file because it's in a variable. How do I get around it. Here is my code that does not work.
siteConf="/etc/apache2/sites-available/$domain.conf"
echo "creating conf file"
echo "<VirtualHost *:80>" >> $siteConf
echo "ServerName *.$domain" >> $siteConf
echo "DocumentRoot $publicHtmlLoc" >> $siteConf
echo "DirectoryIndex index.php" >> $siteConf
echo "ServerAlias $database.newphp.junglecoders.dk" >> $siteConf
echo "</VirtualHost>" >> $siteConf
I am running the script with the bash command.
Edit: The Error i get is this
$siteConf: ambiguous redirect
Domain comes from here:
echo "Write websiter url example.com, no sub dir allowed"
read -p "Name: " domain
As suggested in comments its the path that is wrong, i tried to echo out the variable and i can see it has removed the '.' chars from some reason and left a space instead, why would the script do that ?
Edit2:
Was using IFS earlier in the script, to split the the domain name
The code looks as if it should work. You might do better with
{
echo "<VirtualHost …>"
…
echo "</VirtualHost>"
} > $siteConf
(or >> $siteConf if you really want to add to the existing file). That does a single redirection for all the output, and truncates the file. It is also a good idea as a general rule to enclose uses of variables in double quotes:
} > "$siteConf"
Basic debugging for shell scripts:
What do you get from bash -x your-script.sh?
You get 'ambiguous redirect' when the variable named doesn't exist or is empty, or if it expands to two or more words. That suggests that the first line of your script isn't an accurate representation of what you've got, but it is hard to guess how you've got it wrong. Misspelled name, or an unwanted space are probably the most likely, but it could be something else.
Alright looks like I needed to wrap it like in quotes like in the answer Getting an 'ambiguous redirect' error that antak suggested.
That's a good question to cross-reference. At one point, this question was closed as a duplicate of it, but the issue here turned out to be about IFS being set, which is not an alternative source of trouble identified in that other question.
Have you gone messing with IFS at any point in the script?
Yes — been doing IFS for splitting the URL into parts.
Note that:
set_with_spaces="name1 name2"
echo Hi >> $set_with_spaces
yields
bash: $set_with_spaces: ambiguous redirect too.
Two names were generated from one variable.
$ IFS=.
$ domain=abc.def.ghi.jkl
$ echo $domain
abc def ghi jkl
$ echo "$domain"
abc.def.ghi.jkl
$ IFS=$' \t\n'
$ echo $domain
abc.def.ghi.jkl
$
If you have been messing with IFS, then its current value is probably altering how the names are being interpreted. Reinstate it to its default value (blank, tab, newline):
IFS=$' \t\n'

Ubuntu Bash Script executing a command with spaces

I have a bit of an issue and i've tried several ways to fix this but i can't seem to.
So i have two shell scripts.
background.sh: This runs a given command in the background and redirect's output.
#!/bin/bash
if test -t 1; then
exec 1>/dev/null
fi
if test -t 2; then
exec 2>/dev/null
fi
"$#" &
main.sh: This file simply starts the emulator (genymotion) as a background process.
#!/bin/bash
GENY_DIR="/home/user/Documents/MyScript/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD="$BK player --vm-name $DEVICE"
$CMD
This works fine when i have NO spaces in my directory. However, when i try to do: GENY_DIR="home/user/Documents/My Script/watchdog/genymotion"
which i have no choice at the moment. I get an error saying that the file or directory cannot be found. I tried to put "$CMD" in quote but it didn't work.
You can test this by trying to run anything as a background process, doesn't have to be an emulator.
Any advice or feedback would be appreciated. I also tried to do.
BK="'$BK'"
or
BK="\"$BK\""
or
BK=$( echo "$BK" | sed 's/ /\\ /g' )
Don't try to store commands in strings. Use arrays instead:
#!/bin/bash
GENY_DIR="$HOME/Documents/My Script/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD=( "$BK" "player" --vm-name "$DEVICE" )
"${CMD[#]}"
Arrays properly preserve your word boundaries, so that one argument with spaces remains one argument with spaces.
Due to the way word splitting works, adding a literal backslash in front of or quotes around the space will not have a useful effect.
John1024 suggests a good source for additional reading: I'm trying to put a command in a variable, but the complex cases always fail!
try this:
GENY_DIR="home/user/Documents/My\ Script/watchdog/genymotion"
You can escape the space with a backslash.

Unexpected substitution of characters

I have a script that needs to dynamically assemble another script for later execution, however I'm experiencing problems with characters being substituted, specifically any that are escaped such as \\, \t, \n and so-on. While I can work around this issue with variables, it's extremely annoying, especially as the code is provided in segments wrapped as here documents in quoted form, i.e - such that they should not be processed at all.
Even more annoying, on some platforms this substitution seems to extend to other characters as well, such as \1, which has been causing havoc with my testing of regular expressions.
Here's a simple example:
#!/bin/sh
script=$(cat << 'SCRIPT'
#!/bin/sh
printf '\t%s' "$1"
SCRIPT
)
DIR=$(dirname "$PWD")
echo "$script" > "$DIR/test_script.sh"
I would expect this to produce a simple script with the line printf '\t%s' "$1", however instead the line is produced as printf ' %s' "$1", when no substitution is expected.
Can anyone explain why this is happening, and ideally, how to prevent this from happening? Like I say, I can work around this with variables for substituted characters, but it's destroying the readability of my script (and is hell to debug).
It would appear your version of echo processes some escape characters when you dump the script to a file. echo is not well-standardized, since any standard would have conflicted with some previous implementation. Use printf instead.
#!/bin/sh
script=$(cat << 'SCRIPT'
#!/bin/sh
printf '\t%s' "$1"
SCRIPT
)
DIR=$(dirname "$PWD")
printf "%s\n" "$script" > "$DIR/test_script.sh"

How to execute Linux shell variables within double quotes?

I have the following hacking-challenge, where we don't know, if there is a valid solution.
We have the following server script:
read s # read user input into var s
echo "$s"
# tests if it starts with 'a-f'
echo "$s" > "/home/user/${s}.txt"
We only control the input "$s". Is there a possibility to send OS-commands like uname or do you think "no way"?
I don't see any avenue for executing arbitrary commands. The script quotes $s every time it is referenced, so that limits what you can do.
The only serious attack vector I see is that the echo statement writes to a file name based on $s. Since you control $s, you can cause the script to write to some unexpected locations.
$s could contain a string like bob/important.txt. This script would then overwrite /home/user/bob/important.txt if executed with sufficient permissions. Sorry, Bob!
Or, worse, $s could be bob/../../../etc/passwd. The script would try to write to /home/user/bob/../../../etc/passwd. If the script is running as root... uh oh!
It's important to note that the script can only write to these places if it has the right permissions.
You could embed unusual characters in $s that would cause irregular file names to be created. Un-careful scripts could be taken advantage of. For example, if $s were foo -rf . bar, then the file /home/user/foo -rf . bar.txt would be created.
If someone ran for file in /home/user; rm $file; done they'd have a surprise on their hands. They would end up running rm /home/user/foo -rf . bar.txt, which is a disaster. If you take out /home/user/foo and bar.txt you're left with rm -rf . — everything in the current directory is deleted. Oops!
(They should have quoted "$file"!)
And there are two other minor things which, while I don't know how to take advantage of them maliciously, do cause the script to behave slightly differently than intended.
read allows backslashes to escape characters like space and newline. You can enter \space to embed spaces and \enter to have read parse multiple lines of input.
echo accepts a couple of flags. If $s is -n or -e then it won't actually echo $s; rather, it will interpret $s as a command-line flag.
Use read -r s or any \ will be lost/missinterpreted by your command.
read -r s?"Your input: "
if [ -n "${s}" ]
then
# "filter" file name from command
echo "${s##*/}" | sed 's|^ *\([[:alnum:]_]\{1,\}\)[[:blank:]].*|/home/user/\1.txt|' | read Output
(
# put any limitation on user here
ulimit -t 5 1>/dev/null 2>&1
`${read}`
) > ${OutPut}
else
echo "Bad command" > /home/user/Error.txt
fi
Sure:
read s
$s > /home/user/"$s".txt
If I enter uname, this prints Linux. But beware: this is a security nightmare. What if someone enters rm -rf $HOME? You'd also have issues with commands containing a slash.

Search log file for string with bash script

I just started learning PHP. I'm following phpacademy's tutorials which I would recommend to anyone. Anyways, I'm using XAMPP to test out my scripts. I'm trying to write a bash script that will start XAMPP and then open firefox to the localhost page if it finds a specific string, "XAMPP for Linux started.", that has been redirected from the terminal to the file xampp.log. I'm having a problem searching the file. I keep getting a:
grep: for: No such file or directory
I know the file exists, I think my syntax is wrong. This is what I've got so far:
loaded=$false
string="XAMPP for Linux started."
echo "Starting Xampp..."
sudo /opt/lampp/lampp start 2>&1 > ~/Documents/xampp.log
sleep 15
if grep -q $string ~/Documents/xampp.log; then
$loaded=$true
echo -e "\nXampp successfully started!"
fi
if [$loaded -eq $true]; then
echo -e "Opening localhost..."
firefox "http://localhost/"
else
echo -e "\nXampp failed to start."
echo -e "\nHere's what went wrong:\n"
cat ~/Documents/xampp.log
fi
In shell scripts you shouldn't write $variable, since that will do word expansion on the variable's value. In your case, it results in four words.
Always use quotes around the variables, like this:
grep -e "$string" file...
The -e is necessary when the string might start with a dash, and the quotes around the string keep it as one word.
By the way: when you write shell programs, the first line should be set -eu. This enables *e*rror checking and checks for *u*ndefined variables, which will be useful in your case. For more details, read the Bash manual.
You are searching for a string you should put wihtin quotes.
Try "$string" instead of $string
There are a couple of problems:
quote variables if you want to pass them as a simple argument "$string"
there is no $true and $false
bash does variable expansion, it substitutes variable names with their values before executing the command. $loaded=$true should be loaded=true.
you need spaces and usually quotes in the if: if [$loaded -eq $true] if [ "$loaded" -eq true ]. in this case the variable is set so it won't cause problems but in general don't rely on that.

Resources