bash: asignación de la primera línea de una variable a una variable

11

Tengo una variable multilínea, y solo quiero la primera línea en esa variable. El siguiente script demuestra el problema:

#!/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

Pregunta: ¿Por qué ${STRINGTEST%%$'\n'*}devuelve la primera línea cuando se coloca después de un echocomando, pero reemplaza las nuevas líneas con espacios cuando se coloca después de la asignación?

Almafeta
fuente
1
No se puede reproducir. Funciona para mí como se esperaba.
Pequeños
1
No se puede reproducir con cualquiera de 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 tampoco. Suena como un error del usuario, como intentar ejecutarlo con un shell que no admite en $'...'lugar de bash.
Stéphane Chazelas

Respuestas:

8

Quizás haya otra forma de archivar lo que quieres hacer, pero esto funciona

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
fuente
Eso supone que las líneas $STRINGTESTno contienen espacios en blanco o comodines. También tenga en cuenta que las líneas vacías (como en la primera línea de esa variable) se ignoran.
Stéphane Chazelas
3
También tenga en cuenta que usar STRINGTEST=(${STRINGTEST[@]})tiene poco sentido y es equivalente a STRINGTEST=($STRINGTEST)ya que STRINGTESTse definió previamente como una variable escalar (no matriz ).
Stéphane Chazelas
10

puede que no sea más eficiente, pero un revestimiento ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
fuente
2
Me gusta por claridad y brevedad.
Peter - Restablece a Mónica el
Esto firstLine=`echo "${test_var}" | sed -n 1ptambién funciona si usted tiene una razón para usar sed en su lugar (por ejemplo, significa que se pueden realizar al mismo tiempo un reemplazo de la línea: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Ese código funciona para mí con todas las versiones de bash que probé entre 2.05b y 4.3. Lo más probable es que haya intentado ejecutar ese script con un shell diferente que no admite la $'...'forma de cita.

Que $'...'la sintaxis no es estándar shsintaxis (todavía) y sólo está soportado (a partir del 22/05/2015 y que yo sepa) por ksh93(donde se originó), zsh, bash, las versiones recientes de mkshy los sho las últimas versiones de FreeBSD .

Mi apuesta sería la que ha intentado ejecutar esa secuencia de comandos con el shlugar de bashy tu shse basa en versiones de ash, pdksh, yasho ksh88que no soportan todavía.

Si desea que el código POSIX 2008 sea compatible, deberá escribirlo:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Luego, puede hacer que lo interprete cualquier shell compatible con POSIX basho cualquiera más ágil / rápido como el suyo sh.

(y recuerde que dejar una variable sin comillas en el contexto de la lista tiene un significado muy especial en los shells tipo Bourne).

Stéphane Chazelas
fuente
O podría ser una versión anterior de bash. Podría reproducir el comportamiento con 2.03.
Gilles 'SO- deja de ser malvado'
@Gilles, el último sistema operativo compatible que incluía bash-2.03 (lanzado hace 16 años) fue probablemente Solaris 8, que fue EOL hace más de 3 años. Esa hipótesis parece bastante improbable.
Stéphane Chazelas
1

Esto funciona para mi:

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

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

Y también funciona para líneas en blanco:

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

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
fuente
Si uno se molesta en iniciar la sustitución del proceso, también podría simplemente readuna vez en una variable simple (en lugar de una readarrayindexación positiva).
Peter - Restablece a Mónica el
0

Con Bash incorporado ready here-string:

#!/usr/bin/env bash

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


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

Usando la expansión de parámetros 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
fuente