bash: Zuweisen der ersten Zeile einer Variablen zu einer Variablen


11

Ich habe eine mehrzeilige Variable und möchte nur die erste Zeile in dieser Variablen. Das folgende Skript veranschaulicht das 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

Frage: Warum wird ${STRINGTEST%%$'\n'*}die erste Zeile zurückgegeben, wenn sie nach einem echoBefehl platziert wird, aber Zeilenumbrüche werden durch Leerzeichen ersetzt, wenn sie nach der Zuweisung platziert werden?


1
Kann es nicht reproduzieren. Es funktioniert bei mir wie erwartet.
Peque

1
Kann auch nicht mit 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 reproduziert werden. Klingt nach einem Benutzerfehler, als würde versucht, ihn mit einer Shell auszuführen, die $'...'anstelle von Bash keine Unterstützung bietet .
Stéphane Chazelas

Antworten:


8

Vielleicht gibt es eine andere Möglichkeit, das zu archivieren, was Sie tun möchten, aber das funktioniert

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"

Dies setzt voraus, dass die Zeilen in $STRINGTESTkeine Leerzeichen oder Platzhalter enthalten. Beachten Sie auch, dass leere Zeilen (wie in der ersten Zeile dieser Variablen) ignoriert werden.
Stéphane Chazelas

3
Beachten Sie auch, dass die Verwendung STRINGTEST=(${STRINGTEST[@]})wenig Sinn macht und äquivalent zu ist, STRINGTEST=($STRINGTEST)da sie STRINGTESTzuvor als skalare (nicht Array- ) Variable definiert wurde.
Stéphane Chazelas

10

vielleicht nicht am effizientesten, aber ein Liner ...

firstLine=`echo "${multiLineVariable}" | head -1`

2
Ich mag es aus Gründen der Klarheit und Kürze.
Peter - Reinstate Monica

Dies firstLine=`echo "${test_var}" | sed -n 1pfunktioniert auch, wenn Sie einen Grund haben, stattdessen sed zu verwenden (z. B. bedeutet dies, dass Sie gleichzeitig einen Ersatz für die Leitung durchführen können : echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
Robenkleene

7

Dieser Code funktioniert für mich mit allen Versionen von Bash, die ich zwischen 2.05b und 4.3 ausprobiert habe. Wahrscheinlicher ist, dass Sie versucht haben, dieses Skript mit einer anderen Shell auszuführen, die die $'...'Form des Zitierens nicht unterstützt .

Das $'...'Syntax ist nicht Standard - shSyntax (noch) und nur unterstützt (Stand : 2015.05.22 und AFAIK) durch ksh93(wo sie ihren Ursprung) zsh, die bashjüngsten Versionen mkshund die shoder neuere Versionen von FreeBSD .

Meine Wette wäre, dass Sie versucht, dieses Skript auszuführen mit shstatt bashund Ihr shbasiert auf Versionen ash, pdksh, yashoder ksh88das tun unterstützen es noch nicht.

Wenn Sie diesen Code POSIX 2008-kompatibel machen möchten, müssen Sie ihn schreiben:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Dann können Sie es von jeder POSIX-kompatiblen Shell wie bashoder von jeder schlankeren / schnelleren wie Ihrer interpretieren lassen sh.

(und denken Sie daran, dass das Nicht-Zitieren einer Variablen im Listenkontext in Bourne-ähnlichen Shells eine ganz besondere Bedeutung hat).


Oder es könnte eine ältere Version von Bash sein. Ich konnte das Verhalten mit 2.03 reproduzieren.
Gilles 'SO - hör auf böse zu sein'

@ Gilles, das letzte unterstützte Betriebssystem mit bash-2.03 (veröffentlicht vor 16 Jahren), war wahrscheinlich Solaris 8, das vor über 3 Jahren auf EOL umgestellt wurde. Diese Hypothese scheint ziemlich unwahrscheinlich.
Stéphane Chazelas

1

Das funktioniert bei mir:

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

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

Und es funktioniert auch für Leerzeilen:

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

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

Wenn man sich die Mühe macht, die Prozessersetzung zu starten, kann man auch einfach readeinmal in eine einfache Variable (anstelle der readarrayPlus-Indizierung).
Peter - Reinstate Monica

0

Mit eingebautem Bash readund Here-String:

#!/usr/bin/env bash

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


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

Verwenden der POSIX-Parametererweiterung:

#!/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"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.