13

I a new to bash but I am trying to write a bash script which does the following:

write_to_file()
{
 #check if file exists
 # if not create the file
 # else open the file to edit
 # go in a while loop
 # ask input from user 
 # write to the end of the file
 # until user types  ":q"

 }

If anyone can point out the literature, I would be very thankful Thanks

4
  • 3
    Erm, literature would be man bash. Commented Jan 31, 2013 at 23:50
  • 6
    man bash is more of a reference manual, and a bit challenging for beginners. I think Fraz needs a tutorial. Try tldp.org/LDP/abs/html. Commented Jan 31, 2013 at 23:52
  • 1
    Nice tutorial. If you posted relevant links within that tutorial to address each item requested by Franz, that would be a great answer. Commented Jan 31, 2013 at 23:54
  • 2
    @RandallCook Please, no. To quote the !abs factoid in Freenode's #bash channel: The infamous "Advanced" Bash Scripting Guide should be avoided unless you know how to filter out the junk. It will teach you to write bugs, not scripts. In that light, the BashGuide was written: mywiki.wooledge.org/BashGuide Commented Jan 31, 2013 at 23:59

4 Answers 4

25

Update: As it's a bash question, you should try this first. ;)

cat <<':q' >> test.file

To understand what is going on, read about bash's IO redirection, heredoc syntax and the cat command


As you see above, there are many ways to do it. To explain some more bash commands I've prepared the function also in the way you've requested it:

#!/bin/bash

write_to_file()
{

     # initialize a local var
     local file="test.file"

     # check if file exists. this is not required as echo >> would 
     # would create it any way. but for this example I've added it for you
     # -f checks if a file exists. The ! operator negates the result
     if [ ! -f "$file" ] ; then
         # if not create the file
         touch "$file"
     fi

     # "open the file to edit" ... not required. echo will do

     # go in a while loop
     while true ; do
        # ask input from user. read will store the 
        # line buffered user input in the var $user_input
        # line buffered means that read returns if the user
        # presses return
        read user_input

        # until user types  ":q" ... using the == operator
        if [ "$user_input" = ":q" ] ; then
            return # return from function
        fi

        # write to the end of the file. if the file 
        # not already exists it will be created
        echo "$user_input" >> "$file"
     done
 }

# execute it
write_to_file
Sign up to request clarification or add additional context in comments.

5 Comments

@Fraz Was really fun! :) good question. I had just to follow your comments.
You don't need any of that stuff before the while loop. >> already obeys the correct semantics.
[ "$foo" == "bar" ] isn't POSIX-compliant; POSIX sh only allows = inside of [ ]... and if you're not going to try to be compatible with POSIX, you should be using [[ ]] (which is both more capable and less error-prone).
@CharlesDuffy Thanks for your advices. Will read about this and maybe update the examples
@CarlNorum Yes, thats right. Read my update. I just wanted to show a test for the existence of a file works
6

Example with basic argument checks:

write_to_file()
{
    while [ "$line" != ":q" ]; do
        read line
        if [ "$line" != ":q" ]; then
            printf "%s\n" "$line" >> "$1"
        fi  
    done
}

if [ "$#" -eq 1 ]; then
    write_to_file "$1"
else
    echo "Usage: $0 FILENAME"
    exit 2
fi

Or using the probably lesser known until construct, the function can be written a bit more terse:

# append to file ($1) user supplied lines until `:q` is entered
write_to_file()
{
    until read line && [ "$line" = ":q" ]; do
        printf "%s\n" "$line" >> "$1"
    done
}

7 Comments

Do not use == inside of [ ]; it is not POSIX-compatible (the compatible syntax is only a single =), and doesn't have the extra features and safety conferred by using the non-POSIX [[ ]] extension.
Also, touch "$1" and write_to_file "$1"; the quotes are necessary to avoid string-splitting and glob expansion.
Also, echo $line >> $1 won't work right with a a line such as -e foo (the -e will be treated as an echo argument), and will collapse whitespace ("foo bar" will be changed to "foo bar").
@CharlesDuffy, thanks for the critique; I hope I addressed all your hints.
@CharlesDuffy: Added an even terser version using until. (miku ducks :)
|
2

This quick example should get you started:

while true
do
    read INPUT
    if [[ "${INPUT}" == :q ]]
    then
        return
    fi
    echo "${INPUT}" >> file
done

7 Comments

Not bad. One small enhancement might to be open file only once, when beginning the loop, instead of re-opening it every time one is ready to write another line.
Sure, you could also just concatenate the output into a variable and then write it once at the end. I was going for minimum code & simplicity.
Yes. Safety first and all that. Fixing. Should do that for the test, too.
...well, the test is safe; [[ ]] (unlike [ ]) implicitly prevents string-splitting or glob expansion.
[[ ]] is special syntax. Quotes are genuinely optional there. (If you have more questions on the subject, btw, feel free to pop in in freenode's #bash channel).
|
2

There are several solutions here that are working way too hard. Just do:

write_to_file() { sed '/^:q$/q' | sed '$d' >>"$1"; }

where the first argument is the name of the file. That is, invoke it as:

write_to_file test.file

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.