bash: Gán dòng đầu tiên của một biến cho một biến


11

Tôi có một biến đa dòng và tôi chỉ muốn dòng đầu tiên trong biến đó. Kịch bản sau đây thể hiện vấn đề:

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

Câu hỏi: Tại sao ${STRINGTEST%%$'\n'*}trả về dòng đầu tiên khi được đặt sau một echolệnh, nhưng thay thế dòng mới bằng khoảng trắng khi được đặt sau khi gán?


1
Không thể tái tạo nó. Nó làm việc cho tôi như mong đợi.
Peque

1
Không thể sao chép với bất kỳ 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 nào. Âm thanh giống như lỗi người dùng như cố chạy nó với trình bao không hỗ trợ $'...'thay vì bash.
Stéphane Chazelas

Câu trả lời:


8

Có thể có cách khác để lưu trữ những gì bạn muốn làm, nhưng cách này hiệu quả

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

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

Giả sử các dòng trong $STRINGTESTkhông chứa khoảng trắng hoặc ký tự đại diện. Cũng lưu ý rằng các dòng trống (như trong dòng đầu tiên trong biến đó) bị bỏ qua.
Stéphane Chazelas

3
Cũng lưu ý rằng việc sử dụng STRINGTEST=(${STRINGTEST[@]})làm có ý nghĩa và tương đương với STRINGTEST=($STRINGTEST)từ STRINGTESTtrước đây được định nghĩa là một đại lượng vô hướng (không mảng ) biến.
Stéphane Chazelas

10

có thể không hiệu quả nhất nhưng một lớp lót ...

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

2
Tôi thích nó cho rõ ràng và ngắn gọn.
Peter - Tái lập Monica

Điều này firstLine=`echo "${test_var}" | sed -n 1pcũng hoạt động nếu bạn có lý do để sử dụng sed thay thế (ví dụ: điều đó có nghĩa là bạn có thể đồng thời thực hiện thay thế cho dòng : echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'.
robenkleene

7

Mã đó hoạt động với tôi với tất cả các phiên bản bash tôi đã thử trong khoảng 2.05b đến 4.3. Nhiều khả năng bạn đã cố chạy tập lệnh đó với một trình bao khác không hỗ trợ $'...'hình thức trích dẫn.

Đó là $'...'cú pháp không phải là tiêu chuẩn shcú pháp (chưa) và chỉ được hỗ trợ (như của 2015/05/22 và AFAIK) bởi ksh93(nơi nó có nguồn gốc), zsh, bash, phiên bản gần đây của mkshshhay gần đây phiên bản của FreeBSD .

Đặt cược của tôi sẽ được rằng bạn đã cố gắng để chạy mà kịch bản với shthay bashvà bạn shđược dựa trên phiên bản của ash, pdksh, yashhoặc ksh88không hỗ trợ nó được nêu ra.

Nếu bạn muốn làm cho mã POSIX 2008 tương thích, bạn cần viết nó:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

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

Sau đó, bạn có thể giải thích nó bằng bất kỳ shell tương thích POSIX nào bashhoặc bất kỳ shell nào nhanh hơn / nhanh hơn như của bạn sh.

(và hãy nhớ rằng để lại một biến không được trích dẫn trong ngữ cảnh danh sách có ý nghĩa rất đặc biệt trong các shell giống như Bourne).


Hoặc nó có thể là một phiên bản cũ của bash. Tôi có thể tái tạo hành vi với 2.03.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles, hệ điều hành được hỗ trợ cuối cùng bao gồm bash-2.03 (được phát hành 16 năm trước) có lẽ là Solaris 8 đã đi EOL hơn 3 năm trước. Giả thuyết đó có vẻ khá khó xảy ra.
Stéphane Chazelas

1

Điều này làm việc cho tôi:

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

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

Và nó cũng hoạt động cho các dòng trống:

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

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

Nếu một trong hai điều kiện làm thay đổi quá trình, người ta có thể chỉ cần readmột lần vào một biến đơn giản (thay vì readarraylập chỉ mục cộng).
Peter - Tái lập Monica

0

Với Bash dựng sẵn readvà đây-chuỗi:

#!/usr/bin/env bash

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


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

Sử dụng mở rộng tham số 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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.