Jak odczytać wiele wierszy ze STDIN do zmiennej?


24

Googlowałem to pytanie bezskutecznie. Automatyzuję proces kompilacji tutaj w pracy i wszystko, co próbuję zrobić, to uzyskać numery wersji i krótki opis kompilacji, który może być wieloliniowy. System, na którym działa, to OSX 10.6.8.

Widziałem wszystko, od używania CAT do przetwarzania każdej linii w razie potrzeby. Nie wiem, czego powinienem użyć i dlaczego.

Próbowanie

read -d '' versionNotes

Powoduje zniekształcone wprowadzanie danych, jeśli użytkownik musi użyć klawisza Backspace. Nie ma też dobrego sposobu na zakończenie wprowadzania, ponieważ ^ D się nie kończy, a ^ C po prostu kończy proces.

read -d 'END' versionNotes

Działa ... ale nadal zniekształca dane wejściowe, jeśli potrzebny jest klawisz Backspace.

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

Nie kończy poprawnie danych wejściowych (ponieważ jestem za późno, aby wyszukać dopasowanie do pustego łańcucha).


Otrzymujesz te informacje od użytkownika, prawda?
glenn jackman

Poprawny; Chcę, aby użytkownik wprowadził tę informację w terminalu podczas wykonywania skryptu.
Robert K

1
Nie powiedziałeś się jasno, wolałbym powiedzieć.
poige

Odpowiedzi:


20

man bash wspomina «…

Podstawienie polecenia $ (plik cat) można zastąpić równoważnym, ale szybszym $ (<plik).

… »

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

i zgadzam się, że warto o tym wspomnieć - echo "$myVar"wyświetlałby dane wejściowe tak, jak zostały podane.


1
Jak zatrzymać po wprowadzeniu multilinii? Naciśnięcie Ctrl C powoduje zatrzymanie, ale zmienna nie jest zapisywana!
Nikhil,

2
Terminale UNIX® mają „dyscyplinę”: en.wikipedia.org/wiki/Line_discipline • Sprawdź, co znaczy „eof” • Czytaj man stty, dowiedz się, jak uzyskać aktualne ustawienia
poige

2
Ctrl-D, aby zatrzymać.
freb

9

Spróbuj tego:

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

Bez podziałów linii:

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

Z podziałami linii:

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

2
-d ''jest dokładnie tym, czego potrzebowałem. Dzięki
Bruno Bronosky,

@Bruno Moja przyjemność; to trudne. Zwłaszcza na pierwszy rzut oka wygląda dość standardowo. Może być jednak bardzo przydatny, gdy zrozumiesz różne osobliwości.
głosy


2

Rozwiązałem ten problem, radząc sobie z każdą linią, dopóki nie znalazłem pustej linii. Działa wystarczająco dobrze w mojej sytuacji. Ale jeśli ktoś chce dodać lepsze rozwiązanie, możesz to zrobić.

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

while read line
do
  # break if the line is empty
  [ -z "$line" ] && break
  echo "  $line" >> "source/application.yml"
done

Użyj read -rzamiast tego.
Adapttr

2

Możesz uruchomić edytor, taki jak vim, pico ...

${VISUAL:-${EDITOR:-vi}} source/application.yml

Um ... jak to w jakikolwiek sposób odpowiada na pytanie PO?
Adapttr

Może uruchomić edytor ze skryptu.
Mircea Vutcovici,

I co dokładnie to osiąga? Chce wykorzystać tekst z pliku w skrypcie, a nie zapisu informacji do pliku.
Adapttr

Nie chciał czytać tekstu od użytkownika i zapisywać go w pliku. Przeczytaj komentarze dodane do pytania.
Mircea Vutcovici,

1

Najpierw kilka poprawek:

  1. Aby zezwolić na „edycję” w linii, -ektóra korzysta z readline (abyś miał historię bash i wszystkie funkcje edycji)
  2. -dbierze tylko jedną postać. Np. Z „END” pobiera „E” i ilekroć użytkownik pisze „E”, czytanie zatrzymuje się (myślę, że nie tego chcesz ...)

Można to zrobić na kilka sposobów. Chciałbym czytać wiersz po wierszu i zatrzymywać się po znalezieniu pustej linii (chociaż możesz ustawić dowolne słowo zatrzymania):

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

0

Masz kilka metod.

Jedną z najprostszych metod jest:

MYVAR=$(yourcommand)
echo $"MYVAR"

Na przykład:

MYVAR=$(ls /)
echo $"MYVAR"

Tyle że nie wykonuję polecenia powłoki takiego jak LS. Proszę użytkownika o wprowadzenie wielu wierszy.
Robert K

To bardzo zła praktyka; nie przetwarzaj danych wyjściowych ls!
Adapttr

@adaptr Słyszeliśmy już wszystko, czy mógłbyś zasugerować dobrą alternatywę?
głosy

0

Używamy konstruktów używających xargs i ctrl-d do łamania. Nie jestem z tego w pełni usatysfakcjonowany, ale z pewnością wykonuje zadanie polegające na pobieraniu danych wejściowych od wielu wierszy i umieszczaniu ich w zmiennej (formatowanie nienaruszone). (Pierwsze i trzecie zadanie dodają cudzysłowy wokół zawartości wejścia xargs).

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

Używamy reminderBody jako tematu wiadomości e-mail wysyłanej pocztą (za pośrednictwem bash).


-1

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

Użyj readi zwróć uwagę, że jeśli zakończysz linię z nową linią, \zostanie zignorowana. Więc możesz zrobić:

#! / bin / bash

czytaj -p „wersja:” wersja
wersja echo $

# wprowadź dane wejściowe i zakończ długie linie za pomocą „\”, a następnie po prostu wpisz na końcu
przeczytaj -p „opis:” opis
echo -e "$ wersja \ n $ opis >> twój_plik.txt

Gdybym tylko chciał zignorować akapit z podziałem wiersza, tak. Ale ponieważ jest to wygenerowana lista uwag do wydania dla skryptu kompilacji, muszę być w stanie zachować te podziały wiersza; Na przykład mogę spróbować wpisać listę punktowaną.
Robert K

ABS to potworność o przerażających proporcjach. 90% z nich jest po prostu błędne.
Adapttr

Nie jest tak źle.
głosy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.