bash: Przypisywanie pierwszego wiersza zmiennej do zmiennej

11

Mam zmienną wieloliniową i chcę tylko pierwszy wiersz w tej zmiennej. Poniższy skrypt demonstruje problem:

#!/bin/bash

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

echo "  Take the first line and send to standard output:"
echo ${STRINGTEST%%$'\n'*}
#   Output is as follows:
# Onlygetthefirstline

echo "  Set the value of the variable to the first line of the variable:"
STRINGTEST=${STRINGTEST%%$'\n'*}

echo "  Send the modified variable to standard output:"
echo $STRINGTEST
#   Output is as follows:
# Onlygetthefirstline butnotthesecond orthethird

Pytanie: Dlaczego ${STRINGTEST%%$'\n'*}zwraca pierwszy wiersz po umieszczeniu po echopoleceniu, ale zastępuje nowe wiersze spacjami po umieszczeniu po przypisaniu?

Almafeta
źródło
1
Nie można go odtworzyć. Działa dla mnie zgodnie z oczekiwaniami.
Peque
1
Nie można powielać z żadnym z 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3. Brzmi jak błąd użytkownika, taki jak próba uruchomienia go z powłoką, która nie obsługuje $'...'zamiast bash.
Stéphane Chazelas

Odpowiedzi:

8

Być może istnieje inny sposób archiwizacji tego, co chcesz zrobić, ale to działa

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
źródło
Zakłada się, że wiersze $STRINGTESTnie zawierają spacji ani symboli wieloznacznych. Zauważ też, że puste linie (jak w pierwszym wierszu tej zmiennej) są ignorowane.
Stéphane Chazelas
3
Zauważ też, że używanie STRINGTEST=(${STRINGTEST[@]})nie ma sensu i jest równoważne temu, STRINGTEST=($STRINGTEST)ponieważ STRINGTESTwcześniej było zdefiniowane jako zmienna skalarna (nie macierzowa ).
Stéphane Chazelas
10

może nie być najbardziej wydajny, ale jedna wkładka ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
źródło
2
Lubię to dla jasności i zwięzłości.
Peter - Przywróć Monikę
To firstLine=`echo "${test_var}" | sed -n 1pdziała także jeśli mają powód do korzystania sed zamiast (np Oznacza to można jednocześnie wykonać wymianę do linii: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Ten kod działa dla mnie ze wszystkimi wersjami bash, które wypróbowałem między 2.05b a 4.3. Bardziej prawdopodobne jest, że próbowałeś uruchomić ten skrypt z inną powłoką, która nie obsługuje $'...'formy cytowania.

Że $'...'składnia nie jest standardowa shskładnia (jeszcze) i obsługiwane tylko (od 2015-05-22 i AFAIK) przez ksh93(gdzie to pochodzi) zsh, bashostatnie wersje mkshi shczy nowsze wersje FreeBSD .

Mój zakład jest to, że próbowałeś uruchomić ten skrypt shzamiast bashi twój shoparty jest na wersji ash, pdksh, yashlub ksh88, które nie obsługują jeszcze.

Jeśli chcesz uczynić ten kod POSIX 2008 kompatybilnym, musisz go napisać:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

NL='
'
STRINGTEST=${STRINGTEST%%"$NL"*}
printf '%s\n' "$STRINGTEST"

Następnie możesz go zinterpretować przez dowolną powłokę zgodną z POSIX, jak również bashdowolną cieńszą / szybszą, taką jak twoja sh.

(i pamiętaj, że pozostawienie zmiennej niecytowanej w kontekście listy ma szczególne znaczenie w powłokach podobnych do Bourne'a).

Stéphane Chazelas
źródło
Lub może to być starsza wersja bash. Mógłbym odtworzyć zachowanie za pomocą 2.03.
Gilles „SO - przestań być zły”
@Gilles, ostatnim obsługiwanym systemem operacyjnym, który zawierał bash-2.03 (wydany 16 lat temu), był prawdopodobnie Solaris 8, który przeszedł EOL ponad 3 lata temu. Ta hipoteza wydaje się mało prawdopodobna.
Stéphane Chazelas
1

To działa dla mnie:

STRINGTEST="Some Text 1
Some Text 2
Some Text 3"

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"

Działa również w przypadku pustych linii:

STRINGTEST="
Some Text 1
Some Text 2
Some Text 3"

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
źródło
Jeśli ktoś chce uruchomić proces podstawiania, równie dobrze może po prostu readraz przejść do prostej zmiennej (zamiast readarrayindeksowania plus).
Peter - Przywróć Monikę
0

Z wbudowanym Bash readi tutaj:

#!/usr/bin/env bash

STRINGTEST="
Some Text 1
Some Text 2
Some Text 3"


IFS=$'\n' read -r STRINGTEST <<<"$STRINGTEST"

Korzystanie z rozszerzania parametrów POSIX:

#!/usr/bin/env sh

STRINGTEST="
Some Text 1
Some Text 2
Some Text 3"

# Disables globing
set -f

# Field separator is newline only
IFS="
"

# No quotes, split lines as arguments because of IFS
set -- $STRINGTEST

# First argument is first line
STRINGTEST="$1"
Léa Gris
źródło