41

I've been googling this question to no avail. I'm automating a build process here at work, and all I'm trying to do is get version numbers and a tiny description of the build which may be multi-line. The system this runs on is OSX 10.6.8.

I've seen everything from using CAT to processing each line as necessary. I can't figure out what I should use and why.

Attempts

read -d '' versionNotes

Results in garbled input if the user has to use the backspace key. Also there's no good way to terminate the input as ^D doesn't terminate and ^C just exits the process.

read -d 'END' versionNotes

Works... but still garbles the input if the backspace key is needed.

while read versionNotes
do
  echo "  $versionNotes" >> "source/application.yml"
done

Doesn't properly end the input (because I'm too late to look up matching against an empty string).

3
  • You're getting this information from the user, correct? Apr 9, 2012 at 23:14
  • Correct; I want the user to enter this information in the terminal when executing the script.
    – Robert K
    Apr 10, 2012 at 11:22
  • 2
    You haven't made yourself clear, I'd rather say.
    – poige
    Jun 11, 2012 at 10:33

10 Answers 10

36

man bash mentions «…

The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

…»

$ myVar=$(</dev/stdin)
hello
this is test
$ echo $myVar
hello this is test
$ echo "$myVar"
hello
this is test

and I agree this is worth mentioning — echo "$myVar" would have displayed the input as it was given.

4
  • 1
    How to stop after multiline input? If I press Ctrl C it stops but the variable is not saved!
    – Porcupine
    Aug 13, 2018 at 9:46
  • 2
    UNIX® terminals do have "discipline": en.wikipedia.org/wiki/Line_discipline • Check it out what does "eof" mean • Do read man stty, find out how to get current settings
    – poige
    Aug 13, 2018 at 15:43
  • 3
    ctrl-d to stop.
    – freb
    Oct 23, 2018 at 2:52
  • A complement from this answer, if you want to write directly into a file, simply use echo "$(</dev/stdin)" > /tmp/file.
    – Lalylulelo
    Mar 21, 2023 at 10:19
24

Try this:

user@host:~$ read -d '' x <<EOF
> mic
> check
> one
> two
> EOF

Without line breaks:

user@host:~$ echo $x
mic check one two

With line breaks:

user@host:~$ echo "$x"
mic
check
one
two
2
  • 3
    -d '' is exactly what I needed. Thanks Mar 9, 2017 at 22:53
  • @Bruno My pleasure; it's a tricky one. Especially at first glance, it looks fairly standard. Can be quite useful though, once you get the hang of the various idiosyncrasies.
    – voices
    Mar 11, 2017 at 8:57
9

Refer to the excellent Bash Guide for all your bash scripting needs.

In particular the Bash FAQ contains this at number #1:

How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?

2
  • You say to use read -r, but how do I get it to do so from STDIN? I did attempt to use read -r early on.
    – Robert K
    Apr 25, 2012 at 17:17
  • 1
    It reads from stdin by default.
    – adaptr
    Apr 26, 2012 at 7:17
3

You can start an editor like vim, pico...

${VISUAL:-${EDITOR:-vi}} source/application.yml
4
  • Um... how does that answer the OP's question in any way ?
    – adaptr
    Apr 25, 2012 at 13:27
  • He can start the editor from the script. Apr 25, 2012 at 14:46
  • 1
    And that accomplishes what, exactly ? He wants to use text from a file in the script, not write information to a file.
    – adaptr
    Apr 25, 2012 at 15:05
  • He wasnts to read text from the user and write it to a file. Please read the comments added to the question. Apr 25, 2012 at 16:03
2

I've solved this issue by dealing with each line until I came up with a blank line. It works well enough for my situation. But if someone wants to add a better solution, feel free to do so.

echo "---
notes: |" > 'version.yml'

while read line
do
  # break if the line is empty
  [ -z "$line" ] && break
  echo "  $line" >> "source/application.yml"
done
1
  • 1
    Use read -r instead.
    – adaptr
    Apr 25, 2012 at 13:26
1

First a few corrections:

  1. To allow "edition" on the line use -e which uses readline (so you have the bash history and all editing features)
  2. -d only takes one character. E.g. from 'END' takes 'E' and whenever the user writes an 'E' the reading stops (I guess that's not what you want...)

There are a few possibilities to do this. I'd go for read line by line and stop when an empty line is found (though you could set any stop word):

unset tmp
while :
do 
 read line
 [[ $line == "" ]] && tmp="${tmp:0:$((${#tmp}-1))}" && break
 tmp="$tmp"$line$'\n'
done
1

We are using a construct using xargs and ctrl-d for breaking. I'm not perfectly satisfied with it, but it certainly does the job of taking multi-line user input and stuffing that into a variable (formatting intact). (The first and third assignments add quotes around the contents of the xargs input.)

    printf '%s\n' "When finished hit ctrl-d on a new line to proceed.  " "" "" 
    # this will load the user's input into a variable instead of a file 
    reminderBody="\"" 
    reminderBody+=$( xargs -0 ) 
    reminderBody+="\"" 

We use reminderBody as the body of an e-mail being sent by mail (via bash).

1

As in your comment you are stating that the input comes from a user via terminal: You can use read to read multi line input via

read -d $'\04' versionNotes

which sets the delimiter to EOF, which is 0x04 in unicode so you can read input until CTRL-D is given.

0

You have several methods.

One of the simplest methods is:

MYVAR=$(yourcommand)
echo $"MYVAR"

For example:

MYVAR=$(ls /)
echo $"MYVAR"
3
  • Except I'm not executing a shell command like LS. I'm requesting multi-line input from the user.
    – Robert K
    Apr 10, 2012 at 11:23
  • This is very bad practice; do not process the output of ls!
    – adaptr
    Apr 25, 2012 at 13:14
  • @adaptr We've heard it all before, would you mind suggesting a good alternative, please?
    – voices
    Mar 11, 2017 at 8:50
-1

See: http://tldp.org/LDP/abs/html/internal.html#READR

Use read and note that if you end a line with \ the newline is ignored. So you could do:

#!/bin/bash

read -p "version: " version
echo $version

# enter some input and end long lines with "\", then plain enter at the end
read -p "description: " description
echo -e "$version\n$description >> yourfile.txt
3
  • If I just wanted a paragraph with line breaks ignored, yes. But as this is a generated list of release notes for a build script, I need to be able to preserve those line breaks; I might try to enter a bulleted list, for example.
    – Robert K
    Apr 25, 2012 at 13:09
  • The ABS is a monstrosity of horrific proportions. 90% of it is plain wrong.
    – adaptr
    Apr 25, 2012 at 13:26
  • It's not that bad.
    – voices
    Mar 11, 2017 at 8:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .