Create file with content in one line of bash - bash

I wish to create a single file with some contents known to me.
How do I do this in couple lines of bash?
this command will be used inside of a single script, so it should create file, add text, save, and quit automatically by itself without human intervention.
I know that
cat >> some.text
type some stuff
ctrl + D
will work. But is there a pure command line way of doing it?
Thanks

Use a "here document":
cat >> some.text << 'END'
some stuff here
more stuff
END
The delimiter (here END) is an arbitrary word. Quoting this delimiter after the << will ensure that no expansion is performed on the contents.

You could also do the following:
echo 'some stuff' > your/file.txt
For multiline, here's another example:
printf "some stuff\nmore stuff" >> your/file.txt

For making it multiline its also possilbe to echo in "execution mode":
echo -e "line1\nline2" > /tmp/file
so the \n will make a carriage return.

Great answer from #that-other-guy, also important to note that you can include the directory of the file in there and not to forget your bin/bash stuff at the start, and that it works for more than just text files.
See below my example for yaml files. And remember to make your bash files executable after with: chmod u+x fileName.sh
#!/usr/bin/bash
cat >> ~/dir/newDir/yamlFiles/testing.yaml << 'END'
service:
- testing
testing:
setOpts:
podCount: 2
END

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.

Passing parameters into heredoc

I'm trying to add a service under the name of $1.service, however, the service file being created is not getting the $1, instead, only .service
also there is a part where $1 needs to be pasted inside the $1.service file but it's not passing that information through as well.
this is basically how my bash script look like;
#! /bin/bash
function addService {
cat << EOF > /etc/systemd/system/$1.service
(all that service stuff here)
PIDFile=${_var}/$1.pid
EOF
}
cfg_file=~/config/"$cfg.conf"
if [ -f "$cfg_file" ]; then
. "$cfg_file"
addService $1
fi
so you run the script as ./script.sh test and it should create a service called test.service in this example, but it doesn't seem to be working properly. however, the variables like ${_conf} are passing through without any problems.
and also, do I have to use EOF specifically for this task or echo would do the job alone?
EDIT:
The config file exists and it is $1+.conf and this is the content of test.conf file;
_var=var1
and the .service file that is created passing this information without any problems. which means if $1 wasn't working, it wouldn't fetch the config file as well. but apparently, it is working.
First, you are checking for the existence of a file in ~/conf that ends in .conf? What is the value of $cfg? Does ~/conf/${cfg}.conf exist? If not, are you even going into the if clause? Using "set -x" will help debug these things.
Second, you have EOF indented. For HERE documents, the delimiter must start in the first column. You should have gotten an error when running this script about that. Something like, here-document at line X delimited by end-of-file (wanted EOF). The delimiter string can be anything (e.g. EOSD for end of service definition). It needs to start in column 1 though.
Here is what I quickly did to make sure things work.
#! /bin/bash
set -x
function addService {
cat << EOF > ./$1.service
(all that service stuff here)
PIDFile=${_conf}/$1.pid
EOF
}
cfg_file=./conf.in
if [ -f "$cfg_file" ]; then
. "$cfg_file"
addService $1
fi
Hope this helps.
The problem has been solved by changing
cat << EOF > /etc/systemd/system/$1.service
(service content here)
EOF
to
echo "
(service content here)
" > /etc/systemd/system/$1.service

Why the loop of "While ... do ... done" can't read a text file? [closed]

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]

cat a file inside a shell function

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()

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