bash: affectation de la première ligne d'une variable à une variable

11

J'ai une variable multiligne et je veux seulement la première ligne de cette variable. Le script suivant illustre le problème:

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

Question: Pourquoi ${STRINGTEST%%$'\n'*}retourne la première ligne lorsqu'elle est placée après une echocommande, mais remplace-t-elle les retours à la ligne par des espaces lorsqu'elle est placée après l'affectation?

Almafeta
la source
1
Impossible de le reproduire. Cela fonctionne pour moi comme prévu.
Peque
1
Impossible de reproduire avec l'un des 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 non plus. Cela ressemble à une erreur utilisateur comme essayer de l'exécuter avec un shell qui ne prend pas en charge $'...'au lieu de bash.
Stéphane Chazelas

Réponses:

8

Il y a peut-être une autre façon d'archiver ce que vous voulez faire, mais cela fonctionne

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
la source
Cela suppose que les lignes $STRINGTESTne contiennent ni blancs ni caractères génériques. Notez également que les lignes vides (comme dans la première ligne de cette variable) sont ignorées.
Stéphane Chazelas
3
Notez également que l'utilisation STRINGTEST=(${STRINGTEST[@]})n'a pas de sens et équivaut à, STRINGTEST=($STRINGTEST)car elle STRINGTESTétait précédemment définie comme une variable scalaire (et non un tableau ).
Stéphane Chazelas
10

peut-être pas le plus efficace mais un paquebot ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
la source
2
Je l'aime pour la clarté et la brièveté.
Peter - Réintègre Monica
Cela firstLine=`echo "${test_var}" | sed -n 1pfonctionne aussi si vous avez une raison d'utiliser sed au lieu (par exemple, cela signifie que vous pouvez simultanément effectuer un remplacement à la ligne: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Ce code fonctionne pour moi avec toutes les versions de bash que j'ai essayées entre 2.05b et 4.3. Vous avez probablement tenté d'exécuter ce script avec un autre shell qui ne prend pas en charge la $'...'forme de citation.

Cette $'...'syntaxe est pas standard shsyntaxe (encore) et seulement pris en charge ( à partir de 22/05/2015 et AFAIK) par ksh93(où il est originaire), zsh, bash, les versions récentes de mkshet les shou les versions récentes de FreeBSD .

Mon pari serait que vous avez essayé d'exécuter ce script avec au shlieu de bashet votre shest basé sur les versions de ash, pdksh, yashou ksh88qui ne supportent pas encore.

Si vous souhaitez rendre ce code POSIX 2008 compatible, vous devez l'écrire:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Ensuite, vous pouvez le faire interpréter par n'importe quel shell compatible POSIX comme bashou n'importe quel shell plus léger / plus rapide comme le vôtre sh.

(et rappelez-vous que laisser une variable sans guillemets dans le contexte d'une liste a une signification très spéciale dans les shells de type Bourne).

Stéphane Chazelas
la source
Ou il pourrait s'agir d'une ancienne version de bash. Je pourrais reproduire le comportement avec 2.03.
Gilles 'SO- arrête d'être méchant'
@Gilles, le dernier système d'exploitation pris en charge qui comprenait bash-2.03 (sorti il ​​y a 16 ans) était probablement Solaris 8, qui est passé en fin de vie il y a plus de 3 ans. Cette hypothèse semble assez improbable.
Stéphane Chazelas
1

Cela fonctionne pour moi:

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

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

Et cela fonctionne également pour les lignes vides:

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

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
la source
Si l'on prend la peine de déclencher la substitution de processus, on pourrait aussi bien simplement readune fois dans une simple variable (au lieu de l' readarrayindexation plus).
Peter - Rétablir Monica
0

Avec Bash intégré readet ici-chaîne:

#!/usr/bin/env bash

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


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

Utilisation de l'extension des paramètres 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
la source