
변수 값에서 여러 개의 후행 공백을 제거하려고 할 때 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"
패턴은 단일 공백입니다: . 이에 대한 가장 긴 일치 부분은 바로 단일 공백입니다. 가장 짧은 일치 부분도 바로 단일 공백입니다.
그 이상을 위해서는 globbing 연산자가 필요합니다 *
. 하지만 이는 무엇이든 일치하며 -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