Bash에서: 변수 대체 시 후행 공백 캡처

Bash에서: 변수 대체 시 후행 공백 캡처

변수 값에서 여러 개의 후행 공백을 제거하려고 할 때 BASH 4.3.48(SLES12 SP4) 및 BASH 4.4.23(OpenSUSE Leap 15.1)에서 다음과 같은 현상이 나타납니다.

~> xxx="-O -Wall  "
~> echo "X${xxx%% }X"    # (1)
X-O -Wall X
~> echo "X${xxx%% *}X"
X-OX
~> echo "X${xxx% }X"
X-O -Wall X
~> echo "X${xxx% *}X"    # (2)
X-O -Wall X
~> echo "X${xxx%% \*}X"
X-O -Wall  X

나는 일을 끝내 거나 (1)해야 한다고 생각합니다.(2)

설명서에는 다음과 같이 나와 있습니다 ${parameter%%word}.

일치하는 접미사 패턴을 제거합니다. 경로 이름 확장과 마찬가지로 단어가 확장되어 패턴을 생성합니다. 패턴이 매개변수 확장 값의 후행 부분과 일치하는 경우 확장 결과는 가장 짧은 일치 패턴("%" 케이스) 또는 가장 긴 일치 패턴("%")이 있는 매개변수 확장 값입니다. %'' 사례)이 삭제되었습니다.

문서에 있는 대로(또는 내가 이해한 대로) 작동하지 않기 때문에 이것이 BASH의 버그인 것으로 의심됩니다( -Wall""의 경우 일치하지 않는 접미사("")가 제거됩니다). %% *내가 맞나요?

답변1

에서 echo "X${xxx%% }X"패턴은 단일 공백입니다: . 가장 긴 일치 부분은 공백입니다. 가장 짧은 일치 부분은 공백입니다.

더 많은 경우에는 와일드카드 연산자가 필요합니다 *. 그러나 이것은 무엇이든 일치합니다 -Wall. Bash globbing은 직접적으로 동등한 정규식을 지원하지 않습니다 a*. 당신은해야합니다확장된 와일드카드:

$ shopt -s extglob
$ echo "X${xxx%%+( )}X"
X-O -WallX

답변2

접미사 제거 시 접두사 제거를 사용합니다.

$ xxx="-O -Wall  "
$ echo "X${xxx%"${xxx##*[! ]}"}X"
X-O -WallX
  • 공백이 아닌 마지막 문자까지 모두 제거하고 후행 공백만 남깁니다.
  • 접미사 제거를 위한 패턴으로 이러한 공백을 사용하십시오.
  • 내부 매개변수 확장은 패턴으로 해석되는 것을 방지하기 위해 인용되어야 합니다(위의 내용은 필수는 아니지만 다른 경우에 유용할 수 있습니다).
$ bash -c 'xxx="-O -Wall*   "; echo "X${xxx%%"${xxx##*[! *]}"}X"'
X-O -WallX
$ bash -c 'xxx="-O -Wall*   "; echo "X${xxx%%${xxx##*[! *]}}X"'
XX

이것은 인위적인 예이지만 내부 확장을 인용하지 않으면 포함된 별표는 외부 확장에 의해 쉘 모드로 처리됩니다. 일단 인용하면 말 그대로 별표가 됩니다.


관찰한 동작은 버그가 아니며 단지 쉘 모드가 작동하는 방식에 불과합니다.

${xxx%% }
  • 공간은 공간이다
  • 단일 공백의 가장 긴 발생은 단일 공백입니다.
${xxx%% *}
  • 단일 공백 ​​다음에 아무 것도/아무것도 없는 가장 긴 항목
  • 아무것도 포함하지 않음/아무것도 포함하지 않음-Wall
${xxx% }
  • 단일 공백의 발생 횟수가 가장 짧은 것은 단일 공백입니다.
${xxx% *}
  • 단일 공백 ​​뒤에 아무것도/없음이 나오는 최소 발생 횟수는 단일 공백입니다.
${xxx%% \*}
  • \*백슬래시로 이스케이프 처리된 별표이며 문자 그대로 별표로 해석됩니다.
  • 변수에서 별표 뒤에는 공백이 없으며 접미사는 삭제되지 않습니다.

답변3

read또한 작동할 수도 있습니다( IFS"공간"이 포함되어 있다고 가정).

xxx="-O -Wall  "
read -r xxx <<EOF
$xxx
EOF
echo "X${xxx}X"

산출:

X-O -WallX

  • read다음을 기준으로 입력을 필드로 분할합니다.IFS
  • IFS기본값은 공백/탭/줄 바꿈이므로 선행 및 후행 공백이 모두 제거됩니다.
  • 변수의 첫 번째 줄에 적용됩니다. (여러줄 변수에는 적합하지 않을 수 있으며 bash사용 가능합니다 read -d ''.)

답변4

단순 매개변수 확장은 일치시키고 제거할 수 있는 패턴이 매우 제한되어 있습니다. 문자열 끝에서 여러 (중복) 문자를 제거하려면 일반적인 해결책은 먼저 모든 문자를 제거하는 것입니다.아니요문제가 있는 문자 ${xxx##*[! ]}(모두 후행 공백) 그런 다음 두 번째 단계로 해당 확장에 의해 생성된 모든 것을 끝에서 제거(후행 공백 모두)하면 원하는 결과(후행 공백 제거)가 제공됩니다.

$ xxx="-O -Wall  "
$ echo "<${xxx%"${xxx##*[! ]}"}>"
<-O -Wall>

대안으로 bash에서는 확장 와일드카드를 사용할 수 있습니다.

$ shopt -s extglob
$ echo "<${xxx%%+( )}>"
<-O -Wall>

또는 더 높은 수준의 대안으로 정규 표현식을 사용하여 원하는 것과 일치시킬 수 있습니다.

$ regex='(.*[^ ]) +$';
$ [[ $xxx =~ $regex ]] && echo "<${BASH_REMATCH[1]}>" || echo "<$xxx>"
<-O -Wall>

또는 스크립트로:

#!/bin/bash

xxx=${1:-"-O -Wall  "}

regex='(.*[^ ]) +$'

if    [[ $xxx =~ $regex ]]          # if there are trailing spaces
then 
      echo "<${BASH_REMATCH[1]}>"   # Print the string without spaces
else
      echo "<$xxx>"                 # if there are no trailing spaces.
fi

관련 정보