bash: atribuindo a primeira linha de uma variável a uma variável

11

Eu tenho uma variável multilinha e quero apenas a primeira linha nessa variável. O script a seguir demonstra o 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

Pergunta: Por que ${STRINGTEST%%$'\n'*}retornar a primeira linha quando colocada após um echocomando, mas substituir novas linhas por espaços quando colocadas após a atribuição?

Almafeta
fonte
1
Não é possível reproduzi-lo. Funciona para mim como esperado.
Peque
1
Não é possível reproduzir com nenhum dos 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3. Parece um erro do usuário, como tentar executá-lo com um shell que não suporta, em $'...'vez de bash.
Stéphane Chazelas

Respostas:

8

Talvez exista outra maneira de arquivar o que você deseja fazer, mas isso funciona

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
fonte
Isso pressupõe que as linhas $STRINGTESTnão contenham espaços em branco ou curingas. Observe também que as linhas vazias (como na primeira linha nessa variável) são ignoradas.
Stéphane Chazelas
3
Observe também que o uso STRINGTEST=(${STRINGTEST[@]})faz pouco sentido e é equivalente ao STRINGTEST=($STRINGTEST)que STRINGTESTfoi definido anteriormente como uma variável escalar (não matriz ).
Stéphane Chazelas
10

pode não ser mais eficiente, mas um forro ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
fonte
2
Gosto de clareza e concisão.
Peter - Restabelece Monica
Isso firstLine=`echo "${test_var}" | sed -n 1ptambém funciona se você tem uma razão para usar sed vez (por exemplo, isso significa que você pode, simultaneamente, realizar um substituto para a linha: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Esse código funciona para mim com todas as versões do bash que tentei entre 2.05b e 4.3. Provavelmente você tentou executar esse script com um shell diferente que não suporta a $'...'forma de citação.

Essa $'...'sintaxe não é padrão shsintaxe (ainda) e apenas apoiado (a partir de 2015/05/22 e AFAIK) pelo ksh93(onde se originou), zsh, bash, versões recentes do mkshe shou recentes versões do FreeBSD .

Minha aposta seria que você tentou executar esse script com shem vez de bashe sua shé baseado em versões de ash, pdksh, yashou ksh88que não apoiá-lo ainda.

Se você quiser tornar esse código POSIX 2008 compatível, precisará escrevê-lo:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Em seguida, você pode interpretá-lo por qualquer shell compatível com POSIX, como bashqualquer um mais enxuto / rápido como o seu sh.

(e lembre-se de que deixar uma variável sem aspas no contexto da lista tem um significado muito especial nos shells tipo Bourne).

Stéphane Chazelas
fonte
Ou pode ser uma versão mais antiga do bash. Eu poderia reproduzir o comportamento com o 2.03.
Gilles 'SO- stop be evil'
O @Gilles, o último SO suportado que incluiu o bash-2.03 (lançado há 16 anos) provavelmente foi o Solaris 8, que passou a EOL há mais de 3 anos. Essa hipótese parece bastante improvável.
Stéphane Chazelas
1

Isso funciona para mim:

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

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

E também funciona para linhas em branco:

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

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
fonte
Se alguém se preocupa em iniciar a substituição do processo, também pode ser readuma vez uma variável simples (em vez de readarraymais a indexação).
Peter - Restabelece Monica
0

Com o Bash interno reade a string aqui:

#!/usr/bin/env bash

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


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

Usando a expansão 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
fonte