What are the ============= in the below program? Is that section needed or is it a comment block and can be taken out? I'm somewhat new to UNIX so I am trying to read code to understand it and don't know why this is like this. Also, what is the EOF below it mean. I'm not sure about the meaning of that line either. Can anyone explain please? Thanks in advance
#!/bin/bash
usage() {
cat <<-EOF
========================================================
Usage: $0
Choose either y or n in "do you want to continue"
Choose from option A - E in mainmenu to perform actions.
========================================================
EOF
}
This question "How does “cat << EOF” work in bash?" explains how cat can be used for "here documents".
This question on How can I write a heredoc to a file in Bash script?
also has detailed answers on specifically using cat for this and contains this example that writes the content to a file:
cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
This line is indented.
EOF
From the Advanced Bash-Scripting Guide section on here-docs:
A here document is a special-purpose code block. It uses a form of I/O
redirection to feed a command list to an interactive program or a
command, such as ftp, cat, or the ex text editor.
In this case, the ======================================================== is simply text content to be displayed.
Looking at your code, the heredoc is defined within a method called usage that appears to be called from error_exit(), so I would imagine it's there to display a message about the use of the script to users that input incorrect options.
EOF is an "end of file" - think of it as the beginning and end of the heredoc content.
In answer to your question whether the ======================================================== can be taken out - yes, it can. All that will happen is that it will no longer be displayed to the user.
However, don't remove the EOF!
Related
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.
I am writing a program in which I am taking in a csv file via the < operator on the command line. After I read in the file I would also like to ask the user questions and have them input their response via the command line. However, whenever I ask for user input, my program skips right over it.
When I searched stack overflow I found what seems to be the python version here, but it doesn't really help me since the methods are obviously different.
I read my file using $stdin.read. And I have tried to use regular gets, STDIN.gets, and $stdin.gets. However, the program always skips over them.
Sample input ruby ./bin/kata < items.csv
Current File
require 'csv'
n = $stdin.read
arr = CSV.parse(n)
input = ''
while true
puts "What is your choice: "
input = $stdin.gets.to_i
if input.zero?
break
end
end
My expected result is to have What is your choice: display in the command and wait for user input. However, I am getting that phrase displayed over and over in an infinite loop. Any help would be appreciated!
You can't read both file and user input from stdin. You must choose. But since you want both, how about this:
Instead of piping the file content to stdin, pass just the filename to your script. The script will then open and read the file. And stdin will be available for interaction with the user (through $stdin or STDIN).
Here is a minor modification of your script:
arr = CSV.parse(ARGF) # the important part.
input = ''
while true
puts "What is your choice: "
input = STDIN.gets.to_i
if input.zero?
break
end
end
And you can call it like this:
ruby ./bin/kata items.csv
You can read more about ARGF in the documentation: https://ruby-doc.org/core-2.6/ARGF.html
This has nothing to do with Ruby. It is a feature of the shell.
A file descriptor is connected to exactly one file at any one time. The file descriptor 0 (standard input) can be connected to a file or it can be connected to the terminal. It can't be connected to both.
So, therefore, what you want is simply not possible. And it is not just not possible in Ruby, it is fundamentally impossible by the very nature of how shell redirection works.
If you want to change this, there is nothing you can do in your program or in Ruby. You need to modify how your shell works.
I have recently started studying shell script and I'd like to be able to comment out a set of lines in a shell script. I mean like it is in case of C/Java :
/* comment1
comment2
comment3
*/`
How could I do that?
Use : ' to open and ' to close.
For example:
: '
This is a
very neat comment
in bash
'
Multiline comment in bash
: <<'END_COMMENT'
This is a heredoc (<<) redirected to a NOP command (:).
The single quotes around END_COMMENT are important,
because it disables variable resolving and command resolving
within these lines. Without the single-quotes around END_COMMENT,
the following two $() `` commands would get executed:
$(gibberish command)
`rm -fr mydir`
comment1
comment2
comment3
END_COMMENT
Note: I updated this answer based on comments and other answers, so comments prior to May 22nd 2020 may no longer apply. Also I noticed today that some IDE's like VS Code and PyCharm do not recognize a HEREDOC marker that contains spaces, whereas bash has no problem with it, so I'm updating this answer again.
Bash does not provide a builtin syntax for multi-line comment but there are hacks using existing bash syntax that "happen to work now".
Personally I think the simplest (ie least noisy, least weird, easiest to type, most explicit) is to use a quoted HEREDOC, but make it obvious what you are doing, and use the same HEREDOC marker everywhere:
<<'###BLOCK-COMMENT'
line 1
line 2
line 3
line 4
###BLOCK-COMMENT
Single-quoting the HEREDOC marker avoids some shell parsing side-effects, such as weird subsitutions that would cause crash or output, and even parsing of the marker itself. So the single-quotes give you more freedom on the open-close comment marker.
For example the following uses a triple hash which kind of suggests multi-line comment in bash. This would crash the script if the single quotes were absent. Even if you remove ###, the FOO{} would crash the script (or cause bad substitution to be printed if no set -e) if it weren't for the single quotes:
set -e
<<'###BLOCK-COMMENT'
something something ${FOO{}} something
more comment
###BLOCK-COMMENT
ls
You could of course just use
set -e
<<'###'
something something ${FOO{}} something
more comment
###
ls
but the intent of this is definitely less clear to a reader unfamiliar with this trickery.
Note my original answer used '### BLOCK COMMENT', which is fine if you use vanilla vi/vim but today I noticed that PyCharm and VS Code don't recognize the closing marker if it has spaces.
Nowadays any good editor allows you to press ctrl-/ or similar, to un/comment the selection. Everyone definitely understands this:
# something something ${FOO{}} something
# more comment
# yet another line of comment
although admittedly, this is not nearly as convenient as the block comment above if you want to re-fill your paragraphs.
There are surely other techniques, but there doesn't seem to be a "conventional" way to do it. It would be nice if ###> and ###< could be added to bash to indicate start and end of comment block, seems like it could be pretty straightforward.
After reading the other answers here I came up with the below, which IMHO makes it really clear it's a comment. Especially suitable for in-script usage info:
<< ////
Usage:
This script launches a spaceship to the moon. It's doing so by
leveraging the power of the Fifth Element, AKA Leeloo.
Will only work if you're Bruce Willis or a relative of Milla Jovovich.
////
As a programmer, the sequence of slashes immediately registers in my brain as a comment (even though slashes are normally used for line comments).
Of course, "////" is just a string; the number of slashes in the prefix and the suffix must be equal.
I tried the chosen answer, but found when I ran a shell script having it, the whole thing was getting printed to screen (similar to how jupyter notebooks print out everything in '''xx''' quotes) and there was an error message at end. It wasn't doing anything, but: scary. Then I realised while editing it that single-quotes can span multiple lines. So.. lets just assign the block to a variable.
x='
echo "these lines will all become comments."
echo "just make sure you don_t use single-quotes!"
ls -l
date
'
what's your opinion on this one?
function giveitauniquename()
{
so this is a comment
echo "there's no need to further escape apostrophes/etc if you are commenting your code this way"
the drawback is it will be stored in memory as a function as long as your script runs unless you explicitly unset it
only valid-ish bash allowed inside for instance these would not work without the "pound" signs:
1, for #((
2, this #wouldn't work either
function giveitadifferentuniquename()
{
echo nestable
}
}
Here's how I do multiline comments in bash.
This mechanism has two advantages that I appreciate. One is that comments can be nested. The other is that blocks can be enabled by simply commenting out the initiating line.
#!/bin/bash
# : <<'####.block.A'
echo "foo {" 1>&2
fn data1
echo "foo }" 1>&2
: <<'####.block.B'
fn data2 || exit
exit 1
####.block.B
echo "can't happen" 1>&2
####.block.A
In the example above the "B" block is commented out, but the parts of the "A" block that are not the "B" block are not commented out.
Running that example will produce this output:
foo {
./example: line 5: fn: command not found
foo }
can't happen
Simple solution, not much smart:
Temporarily block a part of a script:
if false; then
while you respect syntax a bit, please
do write here (almost) whatever you want.
but when you are
done # write
fi
A bit sophisticated version:
time_of_debug=false # Let's set this variable at the beginning of a script
if $time_of_debug; then # in a middle of the script
echo I keep this code aside until there is the time of debug!
fi
in plain bash
to comment out
a block of code
i do
:||{
block
of code
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
The job I want to do is reading a line from a text file(currently this file only contains a line, the number of lines will be increased later) with the loop of "While ... do ... done". The weird thing is it can only read some of text files. My code is :
...(previous commands to create "myfile.txt")...
while read -r line
do
echo "flag"
done < "myfile.txt"
I have tried a few cases. If I replaced the "myfile.txt" by another file "test.txt" which is created by hand in current directory(this "test.txt" contains one line either), my script can print "flag".
Similarly, after "myfile.txt" has been created, if I modify and save it in current directory, then run my script, it can print "flag" normally either.
Other cases except above two, my script can't print "flag".
I also tried to "chmod" and "touch" the text file in my script, like following, it can't work either.
Obviously, I hope my script read the line(s) of a text file, can anybody please tell me the reason and give a solution ?
BTW, this file can be read by cat command.
...(previous commands to create "myfile.txt")...
chmod 777 "myfile.txt"
touch "myfile.txt"
cat "myfile.txt" #(I can see the results of this line)
while read -r line
do
echo "flag"
done < "myfile.txt"
Thanks !
the whole code of creating the text file is around 800 lines. However, I'd like to post the lines which create my text file. Here they are:
for(i = 1, i<=6, ++i){
...
ofstream myfile("myfile.txt", std::ios_base::app);
...
if(myfile.is_open()){
myfile << "rms_" << std::setprecision(3) << RMS_values ;
myfile.close();
}
}
**************** Beginning of my solution ****************************************
Thanks for above replies.
I have solved by myself and this link : https://unix.stackexchange.com/questions/31807/what-does-the-noeol-indicator-at-the-bottom-of-a-vim-edit-session-mean
The reason is in my script of producing the text file, there is no "\n" at the end. So, the text file has a "[noeol]" icon after the filename when opened in VI.
According to the above link, if there is no "[noeol]", UNIX/LINUX won't read this file.
The solution is rather simple(looking afterwards), just add "<< "\n" " at the end of "cout". The line becomes,
myfile << "rms_" << std::setprecision(3) << RMS_values << "\n";
**************** End of my solution ****************************************
$ cat test.sh
#!/bnin/bash
echo "content" > "myfile.txt"
cat "myfile.txt" #(I can see the results of this line)
while read -r line
do
echo "flag"
done < "myfile.txt"
$ bash test.sh
content
flag
$
It works. There is no problem with it. The script is exact copy of what you posted except the touch is replaced with some content, because the while loop prints one message per line in the file, so if there are no lines (and touch won't add any), it will obviously print nothing.
I'm taking a guess here:
In Unix, two assumptions are made about text files:
All lines end in a <LF> character. If you edit your file on an old, old Mac which used <CR>, Unix won't see the line endings. If you edit a file on Windows programs like Notepad.exe, your lines will end in <CR><LF> and Unix will assume the <CR> is part of the line.
All lines must end in a <LF>, including the last line. If you write a program using a C program, the last line may not end in a <LF> unless you specifically write it out.
Unix utilities like awk, grep, and shells live and breath on these assumptions. When someone usually tells me something doesn't quite work when reading a file using a shell script, I tell them to edit that file in VIM and then save it (thus forcing an ending <LF> character). In VIM, you need to :set ff=unix and then save. That usually takes care of the issue.
My guess is that your file you're reading in doesn't have the correct line endings, and/or that the last line doesn't have that <LF> character on the end.
I don't really understand your question - can you show us more code/how you create the file?
Here is a working example:
$ cat readfile.sh
#!/bin/bash
{
cat <<EOT
this
is
a
test
file
EOT
} > ./test.txt
while read -r line; do
echo "line = [${line}]"
done <./test.txt
.
$ ./readfile.sh
line = [this]
line = [is]
line = [a]
line = [test]
line = [file]
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).