Bash Variables within text block - bash

I am trying to get variables into a block of text which will later be echoed to a file.
Problem is the $VAR is not getting converted into the variable's value ??
VAR="SOME Value"
read -d '' WPA <<"BLOCK"
Description='WIFI'
Interface=wlan0
Connection=wireless
IP=dhcp
Security=wpa
Key=$VAR
BLOCK
echo "$WPA"
Also, is it possible to append further text to the WPA Block ?

When you quote the delimeter of a heredoc, variables are not interpolated. Just drop the quotes:
read -d '' WPA <<BLOCK
Description='WIFI'
Interface=wlan0
Connection=wireless
IP=dhcp
Security=wpa
Key=$VAR
BLOCK

Why don't you just say
WPA="Description='WIFI'
Interface=wlan0
Connection=wireless
IP=dhcp
Security=wpa
Key=$VAR
"
?
There's not really a need to use read in your case.
If you want to echo append text to $WPA, do it like this:
WPA="$WPA
first appended line
second appended line
"
but be aware that you insert an extra newline this way - $WPA had a newline at the end and there's another one at the beginning of the new text. To avoid this, use
WPA="${WPA}first appended line
second appended line
"
The {} quotation delimits the variable name. Using
WPA="$WPAfirst appended line
would look for a variable named WPAfirst.

is it possible to append further text to the WPA Block ?
$WPA is just a normal shell variable (that happens to contain a multi-line string), so you can append to it with +=; e.g.:
WPA+=$'\nanother line\nand another'
If you wanted to append the content of another heredoc, assign it to a separate variable and append that to WPA (but, as #GuntramBlohm points out, you can just as easily assign/append a multi-line string directly).

Related

How to add bash variable to text file after matched keywords?

I have a file(yaml) with the term "name:" and I would like to add variable($var) from bash and insert into yaml. I am able to find the key words and add variable after that:
sed -i "s/name:/& $var/" "yaml file"
However the variable keep added up in yaml file, as name: abc def ghi(I would like to have single name only)
How to fix it? also how can I add some text after $var, something like "$var-role"?
Thanks.
You need to replace the whole line, not just name:. So add .* to the regexp to match everything after it on the line.
sed -i "s/name:.*/name: $var/" "yaml file"
You can't use & in this version because that would include the rest of the line as well.
If you want to add more text, just put it after the variable.
sed -i "s/name:.*/name: ${var}-role/" "yaml file"
Put {} around the variable name to separate it from the following text (not really needed when the text begins with -, but would be needed if it began with a character that can be part of a variable name).

Unix bash assigning string with spaces to a variable

I am facing issue while assigning sting with spaces to variable. Below is my command.
test.ksh should accept "remove file" as one variable. But I am unable to do so.
hello="/export/appl/<userid>/test.ksh remove file"
$hello
As #Jetchisel mentionned, you should quote your string that contains spaces otherwise it will be interpreted as separated value ($1=remove and $2=file ). Moreover by looking what you want to achieve, I would suggest to use command substitution:
hello=$(/export/appl//test.ksh "remove file")

in Ksh, why does the last (empty) line of my multi-line string disappears when saving it in a variable?

While implementing a script, I am facing the following issue :
when putting the multi-line result of a command into a variable, it seems the last (empty) line of my multi-line string disappear.
This line is "empty", but however, I can not lose the carriage return it contains (because I am concatenating blocks of code saved in DB and containing "\n" character into a human-readable string... If I lose some of the "\n", I will lose a part of my code indentation)
Here is the code to illustrate my issue :
test="A
B
";
test2=`echo "$test"`;
echo "||$test2||";
This returns
||A
B||
while I was expecting :
||A
B
||
--> the last (empty) line has disappeared... and a carriage return is thus missing in my human-readable code.
This issue only occurs when the last line of my multi-line string is empty...
Do you know
Why this last line disappears ?
How I can ensure my last empty line is saved in my multi-line string variable ?
Note that I can of course not use the easiest solution
test2="$test";
because the complete process is rather :
test="^A\n\nB\n^"
test2="`echo "$test" | sed -e 's/\^//g'`";
but I tried to simplify the issue the most I could.
Command substitutions always trim trailing newlines -- that's in accordance with design and specification. If you don't want that, you can append a fixed sigil character to your output and trim it, such that the newlines you want to preserve are before the sigil:
test="A
B
"
test_wip=$(printf '%sEND' "$test")
test2=${test_wip%END}
Instead of trying to work around the issues that arise from assigning the output from echo to a variable (eg, stripping of trailing \n's), consider using ksh's built in string processing in this case, eg:
$ test="^A\n\nB\n^"
$ test2="${test//^}"
$ echo "||${test2}||"
||A
B
||
//^ : remove all ^ characters

Bash script " & " symbol creating an issues

I have a tag
<string name="currencysym">$</string>
in string.xml And what to change the $ symbol dynamically. Use the below command but didn't work:
currencysym=₹
sed -i '' 's|<string name="currencysym">\(.*\)<\/string>|<string name="currencysym">'"<!\[CDATA\[${currencysym}\]\]>"'<\/string>|g'
Getting OUTPUT:
<string name="currencysym"><![CDATA[<string name="currencysym">$</string>#x20B9;]]></string>
" & " Has Removed...
But I need:
<string name="currencysym"><![CDATA[₹]]></string>
using xml-parser/tool to handle xml is first choice
instead of <..>\(.*\)<..> better use <..>\([^<]*\)<..> in case you have many tags in one line
& in replacement has special meaning, it indicates the whole match (\0) of the pattern. That's why you see <...>..</..> came to your output. If you want it to be literal, you should escape it -> \&
First problem is the line
currencysym=₹
This actually reads as "assign empty to currencysym and start no process in the background":
In bash you can set an environment variable (or variables) just or one run of a process by doing VAR=value command. This is how currencysym= is being interpreted.
The & symbol means start process in the background, except there is no command specified, so nothing happens.
Everything after # is interpreted as a comment, so #x20B9; is just whitespace from Bash's point of view.
Also, ; is a command separator, like &, which means "run in foreground". It is not used here because it is commented out by #.
You have to either escape &, # and ;, or just put your string into single quotes: currencysym=\&\#x20B9\; or currencysym='₹'.
Now on top of that, & has a special meaning in sed, so you will need to escape it before using it in the sed command. You can do this directly in the definition like currencysym=\\\&\#x20B9\; or currencysym='\₹', or you can do it in your call to sed using builtin bash functionality. Instead of accessing ${currencysym}, reference ${currencysym/&/\&}.
You should use double-quotes around variables in your sed command to ensure that your environment variables are expanded, but you should not double-quote exclamation marks without escaping them.
Finally, you do not need to capture the original currency symbol since you are going to replace it. You should make your pattern more specific though since the * quantifier is greedy and will go to the last closing tag on the line if there is more than one:
sed 's|<string name="currencysym">[^<]*</string>|<string name="currencysym"><![CDATA['"${currencysym/&/\&}"']]></string>|' test.xml
Yields
<string name="currencysym"><![CDATA[₹]]></string>
EDIT
As #fedorqui points out, you can use this example to show off correct use of capture groups. You could capture the parts that you want to repeat exactly (the tags), and place them back into the output as-is:
sed 's|\(<string name="currencysym">\)[^<]*\(</string>\)|\1<![CDATA['"${currencysym/&/\&}"']]>\2|' test.xml
sed -i '' 's|\(<string name="currencysym">\)[^<]*<|\1<![CDATA[\₹]]><|g' YourFile
the group you keep in buffer is the wrong one in your code, i keep first part not the &
grouping a .* is not the same as all untl first < you need. especially with the goption meaning several occurence could occur and in this case everithing between first string name and last is the middle part (your group).
carrefull with & alone (not escaped) that mean 'whole search pattern find' in replacement part

Bash : read / readarray multiline input

I'm new on bash and I'm trying to write a bash script that will save user's multilines inputs (a text with newlines, somes lines of code, etc.). I need to allow newline (when you press "Enter"), multiline paste (when you paste few lines "Ctrl+V") and set a new key, instead of "Enter", to validate, send the input and continue to the next step of the script.
I tried with read but you can not do multiline.
echo "Enter content :"
read content
I found an example with readarray here (How to delete a character in bash read -d multiline input?) that allow to press "Enter" for newline but each words separate by space are separate in the array. I would like to have only the lines separated.
echo "Enter package names, one per line: hit Ctrl-D on a blank line to stop"
readarray -t pkgs
Do you have any ideas ? Or there is maybe a completely different way to do it ? Thank you for your help.
You can set IFS to newline so that only newlines will separate items in the array.
IFS=$'\n' readarray lines
The first line read will be ${lines[0]}, the second ${lines[1]}, etc. ${#lines[#]} tells you how many lines, and the last one will be ${lines[${#lines[#]}-1]}.
To loop over the array, you should use "${lines[#]}", not ${lines[*]}; the latter will take you right back to looping over individual words.

Resources