들여 쓰기하는 동안 여러 줄의 변수에 문자열 값을 할당하는 방법은 무엇입니까?


54

문제:

  1. 변수에 꽤 긴 값을 할당해야합니다.
  2. 내 스크립트의 모든 줄은 특정 수의 열 아래에 있어야합니다.

그래서 둘 이상의 줄을 사용하여 할당하려고합니다.

들여 쓰기없이 간단하게 수행 할 수 있습니다.

VAR="This displays without \
any issues."
echo "${VAR}"

결과:

This displays without any issues.

그러나 들여 쓰기가있는 경우 :

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"

결과:

This displays with      extra spaces.

이 공백없이 어떻게 우아하게 할당 할 수 있습니까?

답변:


30

여기서 문제는 변수를 큰 따옴표 ( "")로 묶는 것입니다. 제거하면 문제가 해결됩니다.

    VAR="This displays with \
    extra spaces."
    echo ${VAR}

산출

 This displays with extra spaces.

여기서 문제는 변수를 큰 따옴표로 묶으면 모든 공백 문자가 유지된다는 것입니다. 명시 적으로 필요한 경우에 사용할 수 있습니다.

예를 들어

$ echo "Hello     World    ........ ...            ...."

인쇄합니다

Hello     World    ........ ...            ....

따옴표를 제거하면 다른

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....

여기서 Bash는 첫 번째 경우 전체 텍스트가 "단일"인수로 간주되어 추가 공백을 유지하므로 텍스트에서 여분의 공백을 제거합니다. 그러나 두 번째 경우 echo명령은 텍스트를 5 개의 인수로받습니다.

인수를 명령에 전달할 때 변수를 인용하는 것도 도움이됩니다.

아래 명령에서 echo단일 인수 만 가져 옵니다."Hello World"

$ variable="Hello World"
$ echo "$variable"

하지만 아래의 시나리오의 경우 echo로 두 개의 인수를 얻을 수 HelloWorld

$ variable="Hello World"
$ echo $variable

대부분의 답변은 잘 작동했지만 가장 간단했습니다. 감사!
Sman865

12
@ Sman865-이것이 실제로 여기에 제시된 가장 복잡한 답변이며, 대부분의 경우 특히 오프닝 선언에서 잘못되었다고 말할 때 저를 믿어주세요. 가치 할당을 둘러싼 모든 문제는 나중에 확장과 관련이있을 수 없습니다. 죄송합니다. Kannan이지만이 답변은 잘못되었거나 잘못되었습니다. 현장 분할 확장 $IFS은 강력하고 보편적 인 도구이지만 이러한 방식으로 작성된 코드는 어떠한 종류의 신뢰할 수있는 결과도 만들어 낼 수 없습니다.
mikeserv

2
변수를 확장 할 때 따옴표를 사용하지 않으면 파일 이름 확장 (any of * ? [])으로 시작하여 조만간 문제가 발생 합니다.
mr.spuratic

bash 확장을 막기 위해 변수를 큰 따옴표로 묶어야한다는 데 동의합니다. 그러나 특별한 경우 코드의 복잡성을 막기 위해 피할 수 있습니다.
Kannan Mohan

1
@mikeserv에 동의합니다. 액면가로 가져 가면 매우 나쁜 조언입니다. 결과없이 사용하면 파손됩니다. 예를 들어, 변수가 명령의 인수로 전달되면 각 단어가 별도의 인수로 분리되는 것을 원하지 않습니다.
haridsv

28

esuoxuMickaël Bucas 가 제공하는 솔루션 은이 작업을 수행하는 일반적이고 이식 가능한 방법입니다.

다음은 몇 가지 bash솔루션입니다 (일부 솔루션은 다른 쉘에서도 작동해야 함 zsh). 먼저 +=append 연산자를 사용하십시오 (정수 변수, 일반 변수 및 배열 각각에 대해 약간 다른 방식으로 작동 함).

text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
text+="tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
text+="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." 

텍스트에 줄 바꿈 (또는 다른 공백 / 이스케이프)을 원하면 $''따옴표를 대신 사용하십시오.

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'

다음 printf -v으로 형식화 된 값을 변수에 할당하는 데 사용

printf -v text "%s" "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
                    "do eiusmod empor incididunt ut labore et dolore magna aliqua. "\
                    "Ut enim ad minim veniam ..."

여기서 속임수는 형식 지정자보다 많은 인수가 있으므로 대부분의 printf함수 와 달리 bash는 형식 문자열이 끝날 때까지 서식 문자열을 재사용합니다. \n형식 문자열 내에 를 넣거나 $ ''(또는 둘 다)를 사용하여 공백을 처리 할 수 ​​있습니다.

다음으로 배열을 사용하십시오.

text=("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
      "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." )

+=텍스트를 한 줄씩 작성하는 데 사용할 수도 있습니다 (참고 ()).

text+=("post script")

그러나 전체 텍스트 내용을 한 번에 원한다면 배열을 "평평하게"기억해야합니다.

echo "$text"      # only outputs index [0], the first line
echo "${text[*]}" # output complete text (joined by first character of IFS)

(정수 인덱스 배열은 연관 배열과 달리 암시 적으로 정렬됩니다.) 이렇게하면 선을 조작하고 필요한 경우 슬라이스 및 주사위를 만들 수 있기 때문에 약간의 유연성이 제공 됩니다.

마지막으로 reador readarray및 "here-document"를 사용하십시오.

read -r -d '' text <<-"EOT"
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ...
EOT

readarray -t textarray <<-"EOT"
        Lorem [...]
EOT  

여기서 문서 형식은 <<-모든 주요 하드 탭이 입력에서 제거됨 을 의미하므로 텍스트를 들여 쓰려면 탭을 사용해야합니다. 인용 "EOT"부호는 쉘 확장 기능을 방지하므로 입력이 그대로 사용됩니다. 로 read그것을 읽을 수 있도록 그것은, NUL 바이트로 구분 된 입력을 사용하는 줄 바꿈은 한 번에 텍스트를 구분. 로 readarray(일명 mapfile가능한 사람 떠들썩한 파티-4.0)이 배열로 판독하고, -t각 행 바꿈 스트립.


1
read와 옵션은 here document아주 좋은 것입니다! 파이썬 스크립트를 포함하고로 실행하는 데 유용합니다 python -c. 나는 script=$(cat <here document>)전에 했었지만 read -r -d '' script <here document>훨씬 낫다.
haridsv

9

모든 줄의 시작 부분에 을 제거하는 특별한 heredoc 구문이 있습니다 : "<<-"(대시 추가됨)

http://tldp.org/LDP/abs/html/here-docs.html

예 19-4. 탭이 억제 된 여러 줄 메시지

다음과 같이 사용할 수 있습니다.

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"

결과 :

A
B
C

공백이 아닌 탭에서만 작동합니다.


우분투에서는 그 반대가 사실입니다. 탭이 아닌 공백이 작동합니다. \t탭이 필요한 경우 훌륭하게 작동합니다. 그냥 사용 echo -e "$v"하기보다는 echo "$v"백 슬래시 문자를 사용하도록
것 - 산부인과

5

쉘이 원하지 않는 줄 바꿈과 다음 공간을 먹도록하십시오.

$ cat weird.sh 
#!/bin/sh

        var1="A weird(?) $(
             )multi line $(
             )text idea. $(
             )PID=$$"

        var2='You can '$(
            )'avoid expansion '$(
            )'too: PID=$$'

        var3='Or mix it: '$(
            )'To insert the PID use $$. '$(
            )"It expands to e.g. $$."

        echo "$var1"
        echo "$var2"
        echo "$var3"
$ sh weird.sh 
A weird(?) multi line text idea. PID=13960
You can avoid expansion too: PID=$$
Or mix it: To insert the PID use $$. It expands to e.g. 13960.

따라서 가능하지만 ...이 솔루션을 좋아하거나 싫어하는 것은 맛의 문제입니다 ...


3
이 모든 것들은 포크입니다.
mikeserv

5

아마도 당신은 이것을 시도 할 수 있습니다.

          echo "Test" \
               "Test2" \
               "Test3"

5

이것은 내가 당신이 그것을해야한다고 제안하는 방법이며, 그 이유를 설명 할 것이지만, 먼저 다른 것에 대해 이야기하고 싶습니다 ...

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"

여기에있는 다른 많은 해결책은 확장 방법을 변경하여 쉘 변수의 내용에 어떻게 든 영향을 줄 수 있다고 제안하는 것 같습니다. 나는 이것이 사실이 아니라고 확신 할 수있다.

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"

산출

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53

위에서 본 것은 먼저 필드 분할 확장, 확장 소스 변수의 바이트 수에 대한 보고서, 따옴표로 구분 된 확장 및 동일한 바이트 수입니다. 출력은 다를 수 있지만 쉘 변수의 내용은 $string할당을 제외하고 전혀 변경되지 않습니다.

더군다나, 왜 그런지 이해하지 못한다면, 조만간 매우 불쾌한 놀라움에 직면하게 될 것입니다. 다시 시도하지만 약간 다른 조건에서 시도하겠습니다.

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"

동일 $string-다른 환경.

산출

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53

필드 분할은에 정의 된 필드 구분 기호를 기반으로합니다 $IFS. $IFS공백과 $IFS다른 두 가지 구분 기호가 있습니다. 기본적으로 3 개의 가능한 공백 값인 $IFS공간 탭 줄 바꾸기 가 지정 $IFS됩니다. 위에서 볼 수 있듯이 쉽게 변경되며 필드 분할 확장에 큰 영향을 줄 수 있습니다.

$IFS공백은 순서대로 단일 필드로 echo제거되므로 공백을 $IFS포함 할 때 공백 시퀀스를 포함하는 확장 은 단일 공백으로 만 평가됩니다 echo. 공백에 대한 인수를 연결 하기 때문 입니다. 그러나 어떤 공백이 아닌 값은 같은 방식으로 생략하다하지 않으며, 각각의 발생 구분 기호는 항상 자신에게 필드를 얻을 수 -에서 볼 수있는 물건 위의 확장.

이것은 최악이 아닙니다. 이 다른 것을 고려하십시오 $string.

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    

산출

* * * * * * 30
 * * *                  * * *  30

알 겠어? 자, 환경을 다시 바꿔 봅시다.

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    

산출

file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 30
 * * *                  * * *  30

우와

기본적으로 쉘은 파일 이름이 일치 할 경우 파일 이름 글로브를 확장합니다. 이는 매개 변수 확장 및 구문 분석 순서에서 필드 분할 후에 발생 하므로 인용되지 않은 문자열은 이러한 방식으로 취약합니다. 원하는 경우이 동작을 해제 할 수 set -f있지만 POSIX 호환 쉘은 항상 기본적으로 사용됩니다.

이것은 들여 쓰기 환경 설정에 맞게 확장에 따옴표를 놓을 때 대비하는 종류입니다. 그럼에도 불구하고 모든 경우에 확장 동작에 관계없이 실제 값 $string은 항상 마지막으로 할당했을 때의 값입니다 . 첫 번째로 돌아 갑시다.

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
echo "$var" "${#var}"

산출

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70

나는 이것이 쉘 구문을 들여 쓰기 환경 설정에 적용하는 훨씬 더 안전한 방법이라고 생각합니다. 위에서하고있는 것은 각 개별 문자열을 위치 매개 변수에 할당하는 것입니다. 위치 매개 변수는 각각 $1또는 유사한 번호로 참조 할 수 있습니다. ${33}그런 다음 $var특수 쉘 매개 변수 를 사용하여 연결된 값을 할당합니다 $*.

이 접근법은에도 면역이되지 않습니다 $IFS. 그럼에도 불구하고, 나는 이와 관련 $IFS하여 추가 혜택 과의 관계를 고려합니다 . 치다:

IFS=\ ;space_split="$*"
IFS=/; slash_split="$*";IFS='
';new_line_split="$*"

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"

산출

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like.
Arg 1: Line 1./Arg 2: Line 2./and so on for/as long as you might like.
Arg 1: Line 1.
Arg 2: Line 2.
and so on for
as long as you might like.

보다시피 , 첫 번째 바이트에서 $*각 arg를 연결합니다 . 따라서 다르게 할당 된 상태에서 값을 저장하면 저장된 각 값마다 다른 필드 구분 기호가 사용됩니다. 위에서 본 것은 각 변수의 리터럴 값입니다. 구분자를 전혀 원하지 않으면 다음을 수행하십시오."$@"$IFS$IFS

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"

산출

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67

2

시도해 볼 수 있습니다 :

echo $VAR | tr -s " "

또는

myArr=($VAL)
VAL=${myArr[@]}
echo "$VAL"

또한 당신은 확인할 수 있습니다 밖으로.


2

배쉬 대체 확장 사용

Bash를 사용하는 경우 대체 확장을 사용할 수 있습니다 . 예를 들면 다음과 같습니다.

$ echo "${VAR//  /}"
This displays with extra spaces.

1

이것은 경로와 유사한 변수를 설정하는 변형입니다.

set -- "${MYDIR}/usr/local/lib" \
      :"${MYDIR}/usr/lib" \
      :"${MYDIR}/lib" \
       "${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
    export LD_LIBRARY_PATH="$*"
    LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)

다음과 같이 저장하고 나중에 사용할 수있는 set덮어 쓰기 사용$@

ARGV=("$@")
exec foo "${ARGV[@]}"

LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)줄은 콜론 앞의 공백과 가능한 후행 공백을 제거합니다. 후행 공백 만 제거하는 경우 LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }대신 사용하십시오.

이 전체 접근 방식은 mikeserv의 탁월한 답변에 대한 변형입니다.


0

공백 문자를 두려워하지 마십시오. 여러 줄 문자를 인쇄하기 전에 제거하십시오.

$ cat ./t.sh
#!/bin/bash

NEED_HELP=1
if [[ $NEED_HELP -eq 1 ]]; then

    lucky_number=$((1 + RANDOM % 10 + 10))

    read -r -d '' text <<'    EOF'
    NAME says hello

    Look at this helpful text:

                                                 * -**
                                 Eyjafjallajokull        Eyjafjallajokull
                            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

    Don't go away, please rate your experience... It'll just take two minutes.

    EOF
    text=$(echo "$text" | sed -r 's!^\s{4}!!')
    text=$(echo "$text" | sed -r "s!\bNAME\b!$0!") # Bash: text=${text//NAME/$0}
    text=$(echo "$text" | sed -r "s!\btwo\b!$lucky_number!")

    echo "$text"

fi

산출:

$ ./t.sh
./t.sh says hello

Look at this helpful text:

                                             * -**
                             Eyjafjallajokull        Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

Don't go away, please rate your experience... It'll just take 16 minutes.

<<-heredoc 을 사용할 필요없이 탭 문자로 4 칸 들여 쓰기를 깰 필요가 없습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.