How can I write and append using echo command to a file - shell

I am trying to write a script which will use echo and write/append to a file.
But I have " " in syntax already in strings .. say ..
echo "I am "Finding" difficult to write this to file" > file.txt
echo "I can "write" without double quotes" >> file.txt
Can anyone please help to understand this, really appreciated.
BR,
SM

If you want to have quotes, then you must escape them using the backslash character.
echo "I am \"Finding\" difficult to write this to file" > file.txt echo
echo "I can \"write\" without double quotes" >> file.txt
The same holds true if you i.e. also want to write the \ itself, as it may cause side effects. So you have to use \\
Another option would be to use The `'' instead of quotes.
echo 'I am "Finding" difficult to write this to file' > file.txt echo
echo 'I can "write" without double quotes' >> file.txt
However in this case variable substition doesn't work, so if you want to use variables you have to put them outside.
echo "This is a test to write $PATH in my file" >> file.txt
echo 'This is a test to write '"$PATH"' in my file' >> file.txt

If you have special characters, you can escape them with a backslash to use them as needed:
echo "I am \"Finding\" difficult to write this to file" > file.txt
echo "I can \"write\" without double quotes" >> file.txt
However, you can also use the shell's "EOF" feature with the tee command, which is really nice for writing all sorts of things:
tee -a file.txt <<EOF
I am "Finding" difficult to write this to file
I can "write" without double quotes
EOF
That will write virtually ANY content you want directly to that file, and escape any special characters until you get to the EOF.
*Edited to add the append switch, to prevent overwriting the file:
-a

Related

Prevent expansion of \c in echo argument

I'm building a shell script with echo. I have something like:
echo "sed -i \"\\|charlie\.url\\s*=\\s*.*|c\\charlie.url = ${CHARLIE_URL}\" foo.conf" >> bar.sh
i.e. replace the line in foo.conf containing the current charlie.url (not necesarily at the begining, 'cause the line could be commented) for a new line with a new url.
I would expect the output to bar.sh to be
sed -i "\|charlie\.url\s*=\s*.*|c\charlie.url = ${CHARLIE_URL}" foo.conf
Nevertheless, the c\\charlie is interpreted as c \c harlie, instead of
c\ charlie, which generates the following output:
sed -i "\|charlie\.url\s*=\s*.*|c
I have found that I could prevent this by using single instead of doubles quotes, but in that case ${CHARLIE_URL} (which I do need to expand) does not get expanded.
How should my echo argument look like?
I'm using dash (#!/bin/sh under Ubuntu), but I could also use bash or zsh.
Instead of echo, you can try cat :
cat << EOF >> bar.sh
sed -i "\|charlie\.url\s*=\s*.*|c\charlie.url = ${CHARLIE_URL}" foo.conf
EOF
#!/bin/bash
# bash will expand content within "" and stuff like $URL gets expanded
# also double-backslash reduces to a single; bash wouldn't expand stuff within ''
URL="https..."
echo double: "c\\charlie.url, URL $URL"
echo single: 'c\\charlie.url, URL $URL'
# if you need to output a \\
echo "\\\\"
The command line is using your first slash to quote your second one. If CHARLIE_URL=foo, your echo is actually outputting
sed -i "\|charlie\.url\s*=\s*.*|c\charlie.url = foo" foo.conf
Try using single quotes, but close/open them around the variable.
echo 'sed -i "\\|charlie\.url\\s*=\\s*.*|c\\charlie.url = '"${CHARLIE_URL}"'" foo.conf'
This produces
sed -i "\\|charlie\.url\\s*=\\s*.*|c\\charlie.url = foo" foo.conf
You could also, as mentioned, quote the slash that quotes the slash, and then quote the slash being quoted by the other slash, so that the subsequent iterations boil them down to what you want... but that's generally a mess, and leads to leaning-toothpick syndrome.
Someone made a good here-doc suggestion already, so I won't repeat that, but you can also use a "here string" which I generally prefer.
cat <<< "sed -i '\|charlie\.url\s*=\s*.*|c\charlie.url = ${CHARLIE_URL}' foo.conf"
I switched the double-quotes in your script for singles, assuming what you want is to output the literal value of $CHARLIE_URL into your script. If you want the script to use the variable, with whatever value is assigned at runtime, then quote that too -
cat <<< "sed -i '\|charlie\.url\s*=\s*.*|c\charlie.url = \${CHARLIE_URL}' foo.conf"

Handling text containing both quote types

I want to append the following text to a file in Linux:
echo He said "I can't append this" >> file.txt
cat file.txt
He said I can't append this
The closest solution I've found doesn't work. How do I include both sets of quotes in the appended string?
Better to use here-doc to avoid crazy escaping:
cat<<'EOF' > file.txt
He said "I can't append this"
EOF
To avoid the use of cat, you can use printf:
printf 'He said "%s"\n' "I can't append this" >> file.txt
You can concatenate strings as in:
echo He said '"'"I can't append this"'"'
or:
echo 'He said "I can'"'"'t append this"'
but probably the best option is use the \ escape character:
echo 'He said "I can\'t append this"' # note: this is wrong - see comment
EDIT: As noted in #gniourf_gniourf's comment the previous solution with the escape character is wrong. The correct version is
echo "He said \"I can't append this\""

Bash: Echoing a echo command with a variable in bash

Ok, here is one I am struggling with as we speak. Echoing a echo command with a variable.
echo "creating new script file."
echo "#!/bin/bash" > $servsfile
echo "read -p "Please enter a service: " ser " >> $servfile
echo "servicetest=`getsebool -a | grep ${ser}` " >> $servfile
echo "if [ $servicetest > /dev/null ];then " >> $servfile
echo "echo "we are now going to work with ${ser}" " >> $servfile
echo "else" >> $servfile
echo "exit 1" >> $servfile
echo "fi" >> $servfile
My goal is create a script using echo commands then run it later. I just need to figure out how to echo echo/read commands while maintaining my variables.
edit: the variables need to transfer what's inside of them into the new file.
The immediate problem is you have is with quoting: by using double quotes ("..."), your variable references are instantly expanded, which is probably not what you want.
Use single quotes instead - strings inside single quotes are not expanded or interpreted in any way by the shell.
(If you want selective expansion inside a string - i.e., expand some variable references, but not others - do use double quotes, but prefix the $ of references you do not want expanded with \; e.g., \$var).
However, you're better off using a single here-doc[ument], which allows you to create multi-line stdin input on the spot, bracketed by two instances of a self-chosen delimiter, the opening one prefixed by <<, and the closing one on a line by itself - starting at the very first column; search for Here Documents in man bash or at http://www.gnu.org/software/bash/manual/html_node/Redirections.html.
If you quote the here-doc delimiter (EOF in the code below), variable references are also not expanded. As #chepner points out, you're free to choose the method of quoting in this case: enclose the delimiter in single quotes or double quotes, or even simply arbitrarily escape one character in the delimiter with \:
echo "creating new script file."
cat <<'EOF' > "$servfile"
#!/bin/bash
read -p "Please enter a service: " ser
servicetest=`getsebool -a | grep ${ser}`
if [ $servicetest > /dev/null ]; then
echo "we are now going to work with ${ser}"
else
exit 1
fi
EOF
As #BruceK notes, you can prefix your here-doc delimiter with - (applied to this example: <<-"EOF") in order to have leading tabs stripped, allowing for indentation that makes the actual content of the here-doc easier to discern.
Note, however, that this only works with actual tab characters, not leading spaces.
Employing this technique combined with the afterthoughts regarding the script's content below, we get (again, note that actual tab chars. must be used to lead each here-doc content line for them to get stripped):
cat <<-'EOF' > "$servfile"
#!/bin/bash
read -p "Please enter a service name: " ser
if [[ -n $(getsebool -a | grep "${ser}") ]]; then
echo "We are now going to work with ${ser}."
else
exit 1
fi
EOF
Finally, note that in bash even normal single- or double-quoted strings can span multiple lines, but you won't get the benefits of tab-stripping or line-block scoping, as everything inside the quotes becomes part of the string.
Thus, note how in the following #!/bin/bash has to follow the opening ' immediately in order to become the first line of output:
echo '#!/bin/bash
read -p "Please enter a service: " ser
servicetest=$(getsebool -a | grep "${ser}")
if [[ -n $servicetest ]]; then
echo "we are now going to work with ${ser}"
else
exit 1
fi' > "$servfile"
Afterthoughts regarding the contents of your script:
The syntax $(...) is preferred over `...` for command substitution nowadays.
You should double-quote ${ser} in the grep command, as the command will likely break if the value contains embedded spaces (alternatively, make sure that the valued read contains no spaces or other shell metacharacters).
Use [[ -n $servicetest ]] to test whether $servicetest is empty (or perform the command substitution directly inside the conditional) - [[ ... ]] - the preferred form in bash - protects you from breaking the conditional if the $servicetest happens to have embedded spaces; there's NEVER a need to suppress stdout output inside a conditional (whether [ ... ] or [[ ... ]], as no stdout output is passed through; thus, the > /dev/null is redundant (that said, with a command substitution inside a conditional, stderr output IS passed through).
You just need to use single quotes:
$ echo "$TEST"
test
$ echo '$TEST'
$TEST
Inside single quotes special characters are not special any more, they are just normal characters.
echo "echo "we are now going to work with ${ser}" " >> $servfile
Escape all " within quotes with \. Do this with variables like \$servicetest too:
echo "echo \"we are now going to work with \${ser}\" " >> $servfile
echo "read -p \"Please enter a service: \" ser " >> $servfile
echo "if [ \$servicetest > /dev/null ];then " >> $servfile

How can I use a value which includes "" within an echo?

I wrote a bash script which automatically configures a setting file for some application.
The application uses a similar syntax to /etc/sysconfig/network file, the only exception is that it requires the values to be in double quotes " ".
So the line in the script looks something like this, but I don't know how to allow the double quotes of the ip address within the echo:
echo " ipaddr="1.1.1.1" " > file
How can it be done?
Escape the quotes with a backslash character or use single quotes:
echo "ipaddr=\"1.1.1.1\""
echo 'ipaddr="1.1.1.1"'
The other answers all provide excellent ways of solving your problem. I'd just like to add one using printf, that can make things comfortable if, e.g., the ip address is stored in a variable:
ip=1.1.1.1
printf 'ipaddr="%s"\n' "$ip" > file
But here again, as in the other answers, you'll need to play with both, single and double quotes.
Things will be comfortable in this case (still assuming the ip is in the variable ip), because with echo instead of printf, you'd have to use either:
echo "ipaddr=\"$ip\"" > file
# or
echo 'ip addr="'"$ip"'"' > file
(ok, it's not a big deal, but I usually like printf better than echo in bash).
You have at least two options:
Escape the double-quotes with single-quotes:
echo ' ipaddr="1.1.1.1" ' > file
Escape the double-quotes with backslashes:
echo "ipaddr=\"1.1.1.1\"" > file
In general, you can use a backslash to escape any single character; and you can use one type of quote to escape the other.
you got the choice:
you can use different quotes inner and outer of the echo
echo "ipaddr='1.1.1.1'" > file
echo 'ipaddr="1.1.1.1"' > file
you can escape the quotes:
echo "ipaddr=\"1.1.1.1\"" > file
or you can make it simpler (but with escapes on the quotes):
echo ipaddr=\"1.1.1.1\" > file

how to restrain bash from removing blanks when processing file

A simple yet annoying thing:
Using a script like this:
while read x; do
echo "$x"
done<file
on a file containing whitespace:
text
will give me an output without the whitespace:
text
The problem is i need this space before text (it's one tab mostly but not always).
So the question is: how to obtain identical lines as are in input file in such a script?
Update: Ok, so I changed my while read x to while IFS= read x.
echo "$x" gives me correct answer without stripping first tab, but, eval "echo $x" strips this tab.
What should I do then?
read is stripping the whitespace. Wipe $IFS first.
while IFS= read x
do
echo "$x"
done < file
The entire contents of the read are put into a variable called REPLY. If you use REPLY instead of 'x', you won't have to worry about read's word splitting and IFS and all that.
I ran into the same trouble you are having when attempting to strip spaces off the end of filenames. REPLY came to the rescue:
find . -name '* ' -depth -print | while read; do mv -v "${REPLY}" "`echo "${REPLY}" | sed -e 's/ *$//'`"; done
I found the solution to the problem 'eval "echo $x" strips this tab.' This should fix it:
eval "echo \"$x\""
I think this causes the inner (escaped) quotes will be evaluated with the echo, whereas I think that both
eval "echo $x"
and
eval echo "$x"
cause the quotes to be evaluated before the echo, which means that the string passed to echo has no quotes, causing the white space to be lost. So the complete answer is:
while IFS= read x
do
eval "echo \"$x\""
done < file

Resources