39

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

Robert K
  • 572
  • 1
  • 5
  • 12

10 Answers10

35

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.

poige
  • 9,448
  • 2
  • 25
  • 52
  • 1
    How to stop after multiline input? If I press Ctrl C it stops but the variable is not saved! – Porcupine Aug 13 '18 at 09:46
  • 2
    UNIX® terminals do have "discipline": https://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 '18 at 15:43
  • 3
    ctrl-d to stop. – freb Oct 23 '18 at 02:52
  • A complement from [this answer](https://stackoverflow.com/a/42866846/1879269), if you want to write directly into a file, simply use ```echo "$( /tmp/file```. – Lalylulelo Mar 21 '23 at 10:19
22

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
voices
  • 325
  • 2
  • 7
  • 3
    `-d ''` is exactly what I needed. Thanks – Bruno Bronosky Mar 09 '17 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 '17 at 08:57
8

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

adaptr
  • 16,576
  • 23
  • 34
3

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

${VISUAL:-${EDITOR:-vi}} source/application.yml
Mircea Vutcovici
  • 17,619
  • 4
  • 56
  • 83
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
Robert K
  • 572
  • 1
  • 5
  • 12
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.

karlsebal
  • 283
  • 2
  • 14
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
wjandrea
  • 135
  • 7
estani
  • 2,151
  • 2
  • 17
  • 13
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).

JamesIsIn
  • 11
  • 3
0

You have several methods.

One of the simplest methods is:

MYVAR=$(yourcommand)
echo $"MYVAR"

For example:

MYVAR=$(ls /)
echo $"MYVAR"
Andrew Schulman
  • 8,811
  • 21
  • 32
  • 47
Bertrand SCHITS
  • 2,922
  • 1
  • 13
  • 15
-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
wjandrea
  • 135
  • 7
Kindjal
  • 154
  • 4
  • 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 '12 at 13:09
  • The ABS is a monstrosity of horrific proportions. 90% of it is plain wrong. – adaptr Apr 25 '12 at 13:26
  • It's not *that* bad. – voices Mar 11 '17 at 08:45