bash: Menetapkan baris pertama dari suatu variabel ke suatu variabel

11

Saya memiliki variabel multiline, dan saya hanya ingin baris pertama dalam variabel itu. Script berikut menunjukkan masalah:

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

Pertanyaan: Mengapa ${STRINGTEST%%$'\n'*}mengembalikan baris pertama ketika ditempatkan setelah echoperintah, tetapi mengganti baris baru dengan spasi ketika ditempatkan setelah penugasan?

Almafeta
sumber
1
Tidak dapat mereproduksi Ini bekerja untuk saya seperti yang diharapkan.
Peque
1
Tidak dapat mereproduksi dengan 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3. Kedengarannya seperti kesalahan pengguna seperti mencoba menjalankannya dengan shell yang tidak mendukung, $'...'bukan bash.
Stéphane Chazelas

Jawaban:

8

Mungkin ada cara lain untuk mengarsipkan apa yang ingin Anda lakukan, tetapi ini berhasil

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
c4f4t0r
sumber
Itu mengasumsikan garis dalam $STRINGTESTtidak mengandung kartu kosong atau wildcard. Perhatikan juga bahwa baris kosong (seperti pada baris pertama dalam variabel itu) diabaikan.
Stéphane Chazelas
3
Perhatikan juga bahwa menggunakan STRINGTEST=(${STRINGTEST[@]})tidak masuk akal dan setara dengan STRINGTEST=($STRINGTEST)karena STRINGTESTsebelumnya didefinisikan sebagai variabel skalar (bukan array ).
Stéphane Chazelas
10

mungkin tidak paling efisien tetapi satu liner ...

firstLine=`echo "${multiLineVariable}" | head -1`
TacB0sS
sumber
2
Saya suka untuk kejelasan dan singkatnya.
Peter - Reinstate Monica
Ini firstLine=`echo "${test_var}" | sed -n 1pjuga bekerja jika Anda memiliki alasan untuk menggunakan sed bukan (misalnya, itu berarti Anda dapat sekaligus melakukan penggantian ke garis: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene
7

Kode itu berfungsi untuk saya dengan semua versi bash yang saya coba antara 2.05b dan 4.3. Kemungkinan besar Anda mencoba menjalankan skrip itu dengan shell yang berbeda yang tidak mendukung $'...'bentuk kutipan.

Itu $'...'sintaks tidak standar shsintaks (belum) dan hanya didukung (per 2015/05/22 dan AFAIK) oleh ksh93(di mana ia berasal), zsh, bash, versi terbaru dari mkshdan shatau baru versi FreeBSD .

Saya akan bertaruh bahwa Anda mencoba untuk menjalankan bahwa script dengan shalih-alih bashdan Anda shdidasarkan pada versi ash, pdksh, yashatau ksh88yang tidak mendukung itu belum.

Jika Anda ingin membuat kode itu POSIX 2008 kompatibel, Anda harus menuliskannya:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Kemudian, Anda dapat menafsirkannya dengan shell yang sesuai dengan POSIX bashatau yang lebih ramping / lebih cepat seperti milik Anda sh.

(dan ingat bahwa meninggalkan variabel yang tidak dikutip dalam konteks daftar memiliki arti yang sangat istimewa dalam shell mirip Bourne).

Stéphane Chazelas
sumber
Atau bisa juga versi bash yang lebih lama. Saya bisa mereproduksi perilaku dengan 2.03.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles, OS yang didukung terakhir yang mencakup bash-2.03 (dirilis 16 tahun yang lalu) mungkin Solaris 8 yang menjadi EOL lebih dari 3 tahun yang lalu. Hipotesis itu tampaknya sangat tidak mungkin.
Stéphane Chazelas
1

Ini bekerja untuk saya:

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

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

Dan itu juga berfungsi untuk baris kosong:

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

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
moebius_eye
sumber
Jika seseorang mengganggu proses substitusi, ia bisa juga hanya readsekali menjadi variabel sederhana (bukan readarrayplus pengindeksan).
Peter - Pasang kembali Monica
0

Dengan Bash built-in readdan di sini-string:

#!/usr/bin/env bash

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


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

Menggunakan ekspansi parameter 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
sumber