cat a file inside a shell function - shell

I am wondering how I can implement something like the following:
test(){
cat>file<<'EOF'
abc
EOF
}
Many thanks.

Qiang:
Remove the spaces in front of EOF (so it's on a line by itself and not indented).

From bash(1):
If the redirection operator is <<-, then all leading tab
characters are stripped from input lines and the line
containing delimiter. This allows here-documents within
shell scripts to be indented in a natural fashion.
It says tab, and in my testing, tab works, but spaces do not:
#!/bin/bash
cat>file <<-END
hello
world
hello
END
echo done
(All those indents are tabs; the funny thing about the four-leading-spaces markup for code means only four spaces show up in the rendered text, too.)

Your code should work just fine, is there anything specific you are looking for?
#!/bin/sh
input() {
cat > file <<EOF
input
line
another line
EOF
}
input
EDIT: Changed function input to input()

Related

How can I create a line break on Bash while responding to a prompt?

I'm creating a README file using Bash. When adding description in the file, I want the text to appear as 2 paragraphs. How can I create a line break after para one? I tried "\n" but nothing happened.
Continuing from my comments. What you want to be able to write formatted blocks of text out to a file (or to the terminal /dev/stdout) is a heredoc. A heredoc will write the lines out as formatted between and opening and closing tag. (EOF is traditionally used, but it can be anything you like). The form is:
cat << EOF
Your text goes here
and here
and here, etc...
EOF
If you want to write to a file, then use cat >filename << EOF as the opening. If you have variables in your text that you do not want expanded (e.g. $myvar you want written out as $myvar and not what it holds), quote the opening tag, e.g. 'EOF')
In your case if you want to write to a filename from within your script, then just use the form above. You can use default initialization to write to the terminal if no filename is given as an argument to your script, e.g.
#!/bin/bash
fname="${1:-/dev/stdout}" # set filename to write to (stdout by default)
# heredoc
cat >"$fname" << EOF
My dog has fleas and my cat has none. Lucky cat. My snake has
scales and can't have fleas. Lucky snake.
If the animals weren't animals could they still have fleas?
EOF
If called with no argument, the heredoc is printed to the terminal (/dev/stdout). If given a filename, then the heredoc output is redirected to the filename, e.g.
$ bash write-heredoc.sh README
Fills the README file with the heredoc contents, e.g.
$ cat README
My dog has fleas and my cat has none. Lucky cat. My snake has
scales and can't have fleas. Lucky snake.
If the animals weren't animals could they still have fleas?
You can include blank lines as you like. If you want to append to your README file using multiple heredocs, then just use cat >>filename << EOF to append instead of truncate.

Bash - Removing white space from indented multiline strings

This may be a more general question so sorry in advance. I am creating a script and thought it would be good to use multi-line strings instead of using multiple printf or echo statements. Say I have the following:
while :
do
printf "line 1
line 2
line 3"
done
The second and third lines would be printed with a space in front because of the indentation in the file.
l1
line 2
line 3
Is there a way to prevent that aside from removing the indentation on the code? Also, is it considered a better practice to just multiple printf/echo statements if you need to output information that spans multiple lines?
Indent with tabs (here whitespace) and use a heredoc (with <<-)
cat <<- EOF
line 1
line 2
line 3
EOF
Multi-line strings will always look a bit bad, or have some other downsides, I'm afraid. The most legible way to embed them in bash code is probably the here-doc, which shows the string (almost) exactly like it will look when output. As an extra knack, you can use extra punctuation to make the here-doc delimiter to stand out from the string itself too, like so:
if true
then
some commands
cat <<"____EndOfTextBlock____"
This text here
spans multiple
lines.
____EndOfTextBlock____
some other commands
even more commands
fi

Newlines in shell script variable not being replaced properly

Situation: Using a shell script (bash/ksh), there is a message that should be shown in the console log, and subsequently sent via email.
Problem: There are newline characters in the message.
Example below:
ErrMsg="File names must be unique. Please correct and rerun.
Duplicate names are listed below:
File 1.txt
File 1.txt
File 2.txt
File 2.txt
File 2.txt"
echo "${ErrMsg}"
# OK. After showing the message in the console log, send an email
Question: How can these newline characters be translated into HTML line breaks for the email?
Constraint: We must use HTML email. Downstream processes (such as Microsoft Outlook) are too inconsistent for anything else to be of use. Simple text email is usually a good choice, but off the table for this situation.
To be clear, the newlines do not need to be completely removed, but HTML line breaks must be inserted wherever there is a newline character.
This question is being asked because I have already attempted to use several commands, such as sed, tr, and awk with varying degrees of success.
TL;DR: The following snippet will do the job:
ErrMsg=`echo "$ErrMsg"|awk 1 ORS='<br/>'`
Just make sure there are double quotes around the variable when using echo.
This turned out to be a tricky situation. Some notes of explanation are below.
Using sed
Turns out, sed reads through input line by line, which makes finding and replacing those newlines somewhat outside the norm. There were several clever tricks that appeared to work, but I felt they were far too complicated to apply appropriately to this rather simple situation.
Using tr
According to this answer the tr command should work. Unfortunately, this only translates character by character. The two character strings are not the same length, and I am limited to translating the newline into a space or other single character.
For the following:
ErrMsg="Line 1
Line 2
"
ErrMsg=`echo $ErrMsg| tr '\n' 'BREAK'`
# You might expect:
# "Line 1BREAKLine 2BREAK"
# But instead you get:
# "Line 1BLine 2B"
echo "${ErrMsg}"
Using awk
Using awk according to this answer initially appeared to work, but due to some other circumstances with echo there was a subtle problem. The solution is noted in this forum.
You must have double-quotes around your variable, or echo will strip out all newlines.(Of course, awk will receive the characters with a newline at the end, because that's what echo does after it echos stuff.)
This snippet is good: (line breaks in the middle are preserved and replaced correctly)
ErrMsg=`echo "$ErrMsg"|awk 1 ORS='<br/>'`
This snipped is bad: (newlines converted to spaces by echo, one line break at end)
ErrMsg=`echo $ErrMsg|awk 1 ORS='<br/>'`
You can wrap your message in HTML using <pre>, something like
<pre>
${ErrMsg}
and more.
</pre>

Remove colour code special characters from bash file

I have a bash script that runs and outputs to a text file however the colour codes it uses are also included what i'd like to know is how to remove them from the file, ie
^[[38;1;32mHello^[[39m
^[[38;1;31mUser^[[39m
so I just want to be left with Hello and User, so something like sed -r "special characters" from file A save to file B
sed 's/\^\[\[[^m]*m//g'
remove (all) part of line starting with ^[[ until first m
Some like this:
awk '{sub(/\^\[\[38;1;[0-9][0-9]m/,x);sub(/\^\[\[39m/,x)}1'
Hello
User

Block Comments in a Shell Script

Is there a simple way to comment out a block of code in a shell script?
In bash:
#!/bin/bash
echo before comment
: <<'END'
bla bla
blurfl
END
echo after comment
The ' and ' around the END delimiter are important, otherwise things inside the block like for example $(command) will be parsed and executed.
For an explanation, see this and this question.
There is no block comment on shell script.
Using vi (yes, vi) you can easily comment from line n to m
<ESC>
:10,100s/^/#/
(that reads, from line 10 to 100 substitute line start (^) with a # sign.)
and un comment with
<ESC>
:10,100s/^#//
(that reads, from line 10 to 100 substitute line start (^) followed by # with noting //.)
vi is almost universal anywhere where there is /bin/sh.
Use : ' to open and ' to close.
For example:
: '
This is a
very neat comment
in bash
'
This is from Vegas's example found here
You can use:
if [ 1 -eq 0 ]; then
echo "The code that you want commented out goes here."
echo "This echo statement will not be called."
fi
The following should work for sh,bash, ksh and zsh.
The blocks of code to be commented can be put inside BEGINCOMMENT and ENDCOMMENT:
[ -z $BASH ] || shopt -s expand_aliases
alias BEGINCOMMENT="if [ ]; then"
alias ENDCOMMENT="fi"
BEGINCOMMENT
echo "This line appears in a commented block"
echo "And this one too!"
ENDCOMMENT
echo "This is outside the commented block"
Executing the above code would result in:
This is outside the commented block
In order to uncomment the code blocks thus commented, say
alias BEGINCOMMENT="if : ; then"
instead of
alias BEGINCOMMENT="if [ ]; then"
in the example above.
if you can dodge the single quotes:
__='
blah blah comment.
'
In Vim:
go to first line of block you want to comment
shift-V (enter visual mode), up down highlight lines in block
execute the following on selection :s/^/#/
the command will look like this:
:'<,'>s/^/#
hit enter
e.g.
shift-V
jjj
:s/^/#
<enter>
You could use Vi/Vim's Visual Block mode which is designed for stuff like this:
Ctrl-V
Highlight first element in rows you want commented
Shift-i
#
esc
Uncomment would be:
Ctrl-V
Highlight #'s
d
l
This is vi's interactive way of doing this sort of thing rather than counting or reading line numbers.
Lastly, in Gvim you use ctrl-q to get into Visual Block mode rather than ctrl-v (because that's the shortcut for paste).
In all honesty, why so much overengineering...
I consider it really a bad practice to write active code for generating passive code.
My solution: most editors have block select mode. Just use it to add # to all lines you want to comment out.
What's the big deal...
Notepad example:
To create: Alt - mousedrag down, press #.
To delete: Alt-mousedrag down, shift-right arrow, delete.
A variation on the here-doc trick in the accepted answer by sunny256 is to use the Perl keywords for comments. If your comments are actually some sort of documentation, you can then start using the Perl syntax inside the commented block, which allows you to print it out nicely formatted, convert it to a man-page, etc.
As far as the shell is concerned, you only need to replace 'END' with '=cut'.
echo "before comment"
: <<'=cut'
=pod
=head1 NAME
podtest.sh - Example shell script with embedded POD documentation
etc.
=cut
echo "after comment"
(Found on "Embedding documentation in shell script")
You can put the code to comment inside a function. A good thing about this is you can "uncomment" by calling the function just after the definition.
Unless you plan to "uncomment" by calling the function, the text inside the function does not have to be syntactically correct.
ignored() {
echo this is comment
echo another line of comment
}
Many GUI editors will allow you to select a block of text, and press "{" to automatically put braces around the selected block of code.
Let's combine the best of all of these ideas and suggestions.
alias _CommentBegin_=": <<'_CommentEnd_'"
as has been said, the single quote is very important, in that without them
$(commandName) and ${varName} would get evaluated.
You would use it as:
_CommentBegin_
echo "bash code"
or
none code can be in here
_CommentEnd_
The alias makes the usage more obvious and better looking.
I like a single line open and close:
if [ ]; then ##
...
...
fi; ##
The '##' helps me easily find the start and end to the block comment. I can stick a number after the '##' if I've got a bunch of them. To turn off the comment, I just stick a '1' in the '[ ]'. I also avoid some issues I've had with single-quotes in the commented block.
Another mode is:
If your editor HAS NO BLOCK comment option,
Open a second instance of the editor (for example File=>New File...)
From THE PREVIOUS file you are working on, select ONLY THE PART YOU WANT COMMENT
Copy and paste it in the window of the new temporary file...
Open the Edit menu, select REPLACE and input as string to be replaced '\n'
input as replace string: '\n#'
press the button 'replace ALL'
DONE
it WORKS with ANY editor
In vscode ctrl+K+C (ctrl+K+U to uncomment).

Resources