bash: присвоение переменной первой строки переменной

11

У меня есть многострочная переменная, и я хочу только первую строку в этой переменной. Следующий скрипт демонстрирует проблему:

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

Вопрос: Почему ${STRINGTEST%%$'\n'*}возвращается первая строка, когда ставится после echoкоманды, но заменяет символы новой строки пробелами, если ставится после присваивания?

Almafeta
источник
1
Не могу воспроизвести это. Это работает для меня, как ожидалось.
Пек
1
Не может воспроизвести ни с одним из 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 либо. Звучит как ошибка пользователя, как попытка запустить его с помощью оболочки, которая не поддерживает $'...'вместо bash.
Стефан Шазелас

Ответы:

8

Может быть, есть другой способ архивировать то, что вы хотите сделать, но это работает

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
источник
Это предполагает, что строки $STRINGTESTне содержат пробелов или подстановочных знаков. Также обратите внимание, что пустые строки (как в первой строке этой переменной) игнорируются.
Стефан Шазелас
3
Также отметим , что использование STRINGTEST=(${STRINGTEST[@]})имеет мало смысла и эквивалентна STRINGTEST=($STRINGTEST)так STRINGTESTранее была определена как скаляр (не массив ) переменной.
Стефан Шазелас
10

может быть не самый эффективный, но один лайнер ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
источник
2
Мне нравится это для ясности и краткости.
Питер - Восстановить Монику
Это firstLine=`echo "${test_var}" | sed -n 1pтакже работает , если у вас есть причины для использования СЭД вместо (например, это означает , что вы можете одновременно выполнить замену линии: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Этот код работает для меня со всеми версиями bash, которые я пробовал между 2.05b и 4.3. Скорее всего, вы пытались запустить этот скрипт с другой оболочкой, которая не поддерживает $'...'форму цитирования.

Этот $'...'синтаксис не является стандартным shсинтаксисом (пока) и поддерживается только (по состоянию на 2015-05-22 и AFAIK) путем ksh93(где она возникла), zsh, bash, последние версии mkshи shили в последних версиях FreeBSD .

Моя ставка будет то , что вы пытались запустить этот скрипт с shвместо bashи ваш shоснован на версии ash, pdksh, yashили ksh88которые не поддерживают его еще.

Если вы хотите сделать этот код POSIX 2008 совместимым, вам нужно написать его:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Затем вы можете интерпретировать его с помощью любой POSIX-совместимой оболочки, например, bashлюбой более быстрой или более быстрой sh.

(и помните, что оставление переменной без кавычек в контексте списка имеет особое значение для оболочек типа Борна).

Стефан Шазелас
источник
Или это может быть более старая версия bash. Я мог бы воспроизвести поведение с 2.03.
Жиль "ТАК - перестань быть злым"
@Gilles, последней поддерживаемой ОС, включающей bash-2.03 (выпущенной 16 лет назад), была, вероятно, Solaris 8, которая вышла на EOL более 3 лет назад. Эта гипотеза кажется маловероятной.
Стефан Шазелас
1

Это работает для меня:

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

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

И это также работает для пустых строк:

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

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
источник
Если кто-то потрудится запустить подстановку процесса, он может просто readодин раз перейти к простой переменной (вместо readarrayиндексации с плюсом).
Питер - Восстановить Монику
0

Со встроенным Bash readи здесь-строкой:

#!/usr/bin/env bash

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


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

Использование расширения параметров 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"
Леа Гри
источник