Bash 변수에서 공백을 자르는 방법?


920

이 코드가있는 쉘 스크립트가 있습니다.

var=`hg st -R "$path"`
if [ -n "$var" ]; then
    echo $var
fi

그러나 항상 hg st하나 이상의 줄 바꾸기 문자를 인쇄 하기 때문에 조건부 코드는 항상 실행 됩니다.

  • 에서 공백을 제거하는 간단한 방법이 있나요 $var(같은 trim()에서 PHP는 )?

또는

  • 이 문제를 처리하는 표준 방법이 있습니까?

sed 또는 AWK 사용할 수는 있지만이 문제에 대한보다 우아한 해결책이 있다고 생각합니다.


3
관련하여 정수의 공간을 자르고 정수를 얻으려면 $ (($ var))로 감싸고 큰 따옴표 안에 넣을 수도 있습니다. 날짜 설명과 파일 이름을 사용할 때 이것이 중요해졌습니다.
Volomike

"이 문제를 처리하는 표준적인 방법이 있습니까?" 예, [대신 [[를 사용하십시오. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly

도움이된다면 적어도 우분투 16.04에서 테스트하고 있습니다. 다음 일치를 사용하면 모든 방식으로 다듬어 echo " This is a string of char " | xargs집니다.. 그러나 텍스트에 작은 따옴표가 있으면 다음을 수행 할 수 있습니다 echo " This i's a string of char " | xargs -0.. 필자는 최신 xargs (4.6.0)에 대해 언급했습니다.
Luis Alvarado

백틱이 마지막 줄 바꿈을 삼킬 때 줄 바꿈으로 인해 조건이 사실이 아닙니다. 이것은 아무것도 출력하지 test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi않지만 test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi, 결국에는 개행 문자 이상이 있어야합니다.
Mecki

A = "123 4 5 6"; B = echo $A | sed -r 's/( )+//g';
bruziuz

답변:


1021

선행, 후행 및 중간 공백이 포함 된 변수를 정의 해 보겠습니다.

FOO=' test test test '
echo -e "FOO='${FOO}'"
# > FOO=' test test test '
echo -e "length(FOO)==${#FOO}"
# > length(FOO)==16

어떻게 (로 표시 모든 공백을 제거하는 방법 [:space:]에를 tr)

FOO=' test test test '
FOO_NO_WHITESPACE="$(echo -e "${FOO}" | tr -d '[:space:]')"
echo -e "FOO_NO_WHITESPACE='${FOO_NO_WHITESPACE}'"
# > FOO_NO_WHITESPACE='testtesttest'
echo -e "length(FOO_NO_WHITESPACE)==${#FOO_NO_WHITESPACE}"
# > length(FOO_NO_WHITESPACE)==12

선행 공백 만 제거하는 방법 :

FOO=' test test test '
FOO_NO_LEAD_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//')"
echo -e "FOO_NO_LEAD_SPACE='${FOO_NO_LEAD_SPACE}'"
# > FOO_NO_LEAD_SPACE='test test test '
echo -e "length(FOO_NO_LEAD_SPACE)==${#FOO_NO_LEAD_SPACE}"
# > length(FOO_NO_LEAD_SPACE)==15

후행 공백 만 제거하는 방법 :

FOO=' test test test '
FOO_NO_TRAIL_SPACE="$(echo -e "${FOO}" | sed -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_TRAIL_SPACE='${FOO_NO_TRAIL_SPACE}'"
# > FOO_NO_TRAIL_SPACE=' test test test'
echo -e "length(FOO_NO_TRAIL_SPACE)==${#FOO_NO_TRAIL_SPACE}"
# > length(FOO_NO_TRAIL_SPACE)==15

선행 및 후행 공백을 제거하는 방법-체인을 연결하십시오 sed.

FOO=' test test test '
FOO_NO_EXTERNAL_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_EXTERNAL_SPACE='${FOO_NO_EXTERNAL_SPACE}'"
# > FOO_NO_EXTERNAL_SPACE='test test test'
echo -e "length(FOO_NO_EXTERNAL_SPACE)==${#FOO_NO_EXTERNAL_SPACE}"
# > length(FOO_NO_EXTERNAL_SPACE)==14

배시가 지원하는 경우 또는, 당신은 대체 할 수 echo -e "${FOO}" | sed ...sed ... <<<${FOO}과 같이 (공백을 후행) :

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"

63
모든 형태의 공백 을 처리하도록 솔루션을 일반화하려면 trsed명령 에서 공백 문자를로 바꿉니다 [[:space:]]. 이 sed접근법은 단일 라인 입력 에서만 작동 합니다. 멀티 라인 입력으로 작동하고 bash의 내장 기능을 사용하는 접근 방법은 @bashfu 및 @GuruM의 답변을 참조하십시오. @Nicholas Sushkin 솔루션의 일반화 된 인라인 버전은 다음과 같습니다. trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0

7
자주 그렇게 할 경우, 추가 alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"사용자로하는 것은 ~/.profile당신이 사용할 수 있도록 echo $SOMEVAR | trim하고 cat somefile | trim.
instanceof me

나는 sed두 개가 아닌 단일 표현식 만 사용 하는 솔루션을 작성했습니다 sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. 선행 및 후행 공백을 자르고 중간에서 공백으로 구분 된 비 공백 문자 시퀀스를 캡처합니다. 즐겨!
Victor Zamanian

@VictorZamanian 입력에 공백 만 있으면 솔루션이 작동하지 않습니다. MattyV와 내 인스턴스가 제공하는 2 패턴 sed 솔루션은 공백 전용 입력으로 잘 작동합니다.
Torben

@Torben Fair 포인트. 단일 표현식을 여러 조건이 |아닌 하나의 단일 표현식으로 유지하기 위해 조건부로 만들 수 있다고 가정합니다 .
Victor Zamanian

965

간단한 대답은 다음과 같습니다.

echo "   lol  " | xargs

Xargs 가 트리밍을 수행합니다. 매개 변수가없는 하나의 명령 / 프로그램이므로 손질 된 문자열을 쉽게 반환합니다!

참고 : 이렇게하면 모든 내부 공간이 제거되지 않으므로 "foo bar"그대로 유지됩니다. 되지 않습니다 "foobar". 그러나, 여러 공간이 하나의 공간으로 응축 될 것이다, 그렇게 "foo bar"될 것입니다 "foo bar". 또한 줄 끝 문자를 제거하지 않습니다.


27
좋은. 이것은 정말 잘 작동합니다. 나는 xargs echo내가하고있는 일에 대해 장황 하게 파이프하기로 결정 했지만 xargs 자체는 기본적으로 echo를 사용합니다.
Will

24
좋은 속임수이지만주의해서 한 줄짜리 문자열에 사용할 수 있지만 xargs 디자인에서는 여러 줄로 된 파이프 내용으로 트리밍하는 것이 아닙니다. sed는 당신의 친구입니다.
Jocelyn delalande

22
xargs의 유일한 문제는 개행을 도입한다는 것입니다. 개행을 유지하지 않으려면 sed 's/ *$//'대안으로 권장 합니다. 당신이 볼 수있는 xargs이 같은 줄 바꿈 : echo -n "hey thiss " | xargs | hexdump 당신이 알 0a73(가) a줄 바꿈입니다. 당신이 동일한 작업을 수행하는 경우 sed: echo -n "hey thiss " | sed 's/ *$//' | hexdump당신은 볼 수 없습니다 0073, 더 줄 바꿈.

8
꼼꼼한; xargs에 대한 문자열에 사이에 여분의 공백이 포함되어 있으면 열심히 작동합니다. "이것은 하나의 논쟁"입니다. xargs는 4 개로 나뉩니다.
bos

64
이것은 나쁘다. 1. a<space><space>b로 바뀝니다 a<space>b. 2. 더욱 :이 켜집니다 a"b"c'd'eabcde. 3. 더 : 실패 할 것입니다 a"b.
Sasha

357

와일드 카드 라는 Bash 내장 기능 만 사용하는 솔루션이 있습니다 .

var="    abc    "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"   
printf '%s' "===$var==="

다음은 함수에 같은 내용이 포함되어 있습니다.

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   
    printf '%s' "$var"
}

따옴표로 묶은 문자열을 전달합니다. 예 :

trim "   abc   "

이 솔루션의 좋은 점은 POSIX 호환 쉘에서 작동한다는 것입니다.

참고


17
영리한! 이것은 내장 bash 기능을 사용하기 때문에 내가 가장 좋아하는 솔루션입니다. 게시 해 주셔서 감사합니다! @ San, 두 개의 중첩 된 문자열 트림입니다. 예를 들어, s=" 1 2 3 "; echo \""${s%1 2 3 }"\"끝에서 모든 것을 다듬어 선행을 반환합니다 " ". 1 2 3 와 함께 subbing [![:space:]]*하면 "첫번째 공백이 아닌 문자를 찾은 다음 그 뒤에 모든 것을 클로버 합니다"라고 지시합니다. 사용 %%대신 %트림-에서 엔드 작업이 욕심이 있습니다. 이것은 시작이 아닌 욕심없는 트림에서 중첩되므로 사실상 " "시작부터 트림 합니다. 그런 다음 끝 공간에 대해 %, # 및 *를 바꾸십시오. 밤!
Mark G.

2
원치 않는 부작용을 발견하지 못했고 주 코드는 다른 POSIX와 같은 쉘에서도 작동합니다. 그러나 Solaris 10에서는 작동하지 않습니다 /bin/sh(에서만 사용 /usr/xpg4/bin/sh되지만 일반적인 sh 스크립트와 함께 사용되는 것은 아닙니다).
vinc17

9
포크 ()를 피하면서 훨씬 빠르기 때문에 sed, tr 등을 사용하는 것보다 훨씬 나은 솔루션입니다. Cygwin에서 속도의 차이는 수십 배입니다.
유전자 Pavlovsky

9
@San 처음에는 정규 표현식이라고 생각했기 때문에 혼란에 빠졌습니다. 그들은 아닙니다. 대신 이것은 하위 문자열 제거 ( tldp.org/LDP/abs에 사용되는 패턴 일치 구문 ( gnu.org/software/bash/manual/html_node/Pattern-Matching.html , wiki.bash-hackers.org/syntax/pattern )입니다) /html/string-manipulation.html ). 그래서 ${var%%[![:space:]]*}"에서 제거 말한다 var가장 긴 문자열이 아닌 공백 문자로 시작"는. 즉, 앞에 공백 만 남겨두고 나중에 제거합니다 ${var#... 다음 줄 (트레일 링)은 반대입니다.
Ohad Schneider

8
이것은 압도적으로 이상적인 솔루션입니다. 하나 이상의 외부 프로세스를 포크 (fork) (예를 들어, awk, sed, tr, xargs) 단지 하나의 문자열에서 트림 공백을 근본적으로 미친 - 대부분의 쉘 (bash는 포함) 할 때 특히 이미 아웃 - 오브 - 박스 기능을 munging 기본 문자열을 제공합니다.
세실 커리

81

Bash에는 매개 변수 확장 이라는 기능이 있는데, 그 중에서도 소위 패턴을 기반으로 문자열을 대체 할 수 있습니다 (패턴은 정규식과 유사하지만 근본적인 차이점과 한계가 있습니다). [flussence의 원래 줄 : Bash에는 정규 표현식이 있지만 잘 숨겨져 있습니다.]

다음 은 변수 값에서 공백을 모두 제거하는 방법을 보여줍니다 .

$ var='abc def'
$ echo "$var"
abc def
# Note: flussence's original expression was "${var/ /}", which only replaced the *first* space char., wherever it appeared.
$ echo -n "${var//[[:space:]]/}"
abcdef

2
또는 오히려 var 중간의 공백에는 효과가 있지만 끝에 고정하려고하면 아닙니다.
Paul Tomblin

이것이 도움이 되나요? 맨 페이지에서 : "$ {parameter / pattern / string} [...] 패턴이 %로 시작하면 확장 된 매개 변수 값 끝에서 일치해야합니다."

@Ant, 그래서 그들은 정규 표현식이 아니지만 비슷한 것이 있습니까?
Paul Tomblin

3
그들은 정규식이며 이상한 방언입니다.

13
${var/ /}첫 번째 공백 문자를 제거합니다. ${var// /}모든 공백 문자를 제거합니다. 이 구문만으로 선행 및 후행 공백을 트리밍 할 수있는 방법이 없습니다.
질 'SO-정지 존재 악마'

60

문자열의 시작과 끝에서 모든 공백을 제거하려면 (줄 끝 문자 포함) :

echo $variable | xargs echo -n

중복 된 공간도 제거됩니다 :

echo "  this string has a lot       of spaces " | xargs echo -n

생성 : '이 문자열에는 많은 공간이 있습니다'


5
기본적으로 xargs는 문자열에서 모든 분리 문자를 제거합니다. 기본적으로 공백을 구분 기호로 사용합니다 (-d 옵션으로 변경 가능).
rkachach

4
이것은 가장 깨끗하고 (짧고 읽기 쉬운) 솔루션입니다.
Potherca

왜 당신 echo -n은 전혀 필요 합니까? echo " my string " | xargs동일한 출력을가집니다.
bfontaine

echo -n도 줄의 끝을 제거합니다
rkachach

55

하나의 선행 및 후행 공간 제거

trim()
{
    local trimmed="$1"

    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"

    echo "$trimmed"
}

예를 들면 다음과 같습니다.

test1="$(trim " one leading")"
test2="$(trim "one trailing ")"
test3="$(trim " one leading and one trailing ")"
echo "'$test1', '$test2', '$test3'"

산출:

'one leading', 'one trailing', 'one leading and one trailing'

스트립 모든 선행 및 후행 공백을

trim()
{
    local trimmed="$1"

    # Strip leading spaces.
    while [[ $trimmed == ' '* ]]; do
       trimmed="${trimmed## }"
    done
    # Strip trailing spaces.
    while [[ $trimmed == *' ' ]]; do
        trimmed="${trimmed%% }"
    done

    echo "$trimmed"
}

예를 들면 다음과 같습니다.

test4="$(trim "  two leading")"
test5="$(trim "two trailing  ")"
test6="$(trim "  two leading and two trailing  ")"
echo "'$test4', '$test5', '$test6'"

산출:

'two leading', 'two trailing', 'two leading and two trailing'

9
공백 문자 1 개만 자릅니다. 에코가 결과'hello world ', 'foo bar', 'both sides '
Joe

@ 조 더 나은 옵션을 추가했습니다.
wjandrea

42

장갑판의 배쉬 가이드 섹션에서

매개 변수 확장에서 extglob을 사용하려면

 #Turn on extended globbing  
shopt -s extglob  
 #Trim leading and trailing whitespace from a variable  
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}  
 #Turn off extended globbing  
shopt -u extglob  

함수에 래핑 된 동일한 기능은 다음과 같습니다 (참고 : 함수에 전달 된 입력 문자열을 인용해야 함).

trim() {
    # Determine if 'extglob' is currently on.
    local extglobWasOff=1
    shopt extglob >/dev/null && extglobWasOff=0 
    (( extglobWasOff )) && shopt -s extglob # Turn 'extglob' on, if currently turned off.
    # Trim leading and trailing whitespace
    local var=$1
    var=${var##+([[:space:]])}
    var=${var%%+([[:space:]])}
    (( extglobWasOff )) && shopt -u extglob # If 'extglob' was off before, turn it back off.
    echo -n "$var"  # Output trimmed string.
}

용법:

string="   abc def ghi  ";
#need to quote input-string to preserve internal white-space if any
trimmed=$(trim "$string");  
echo "$trimmed";

서브 쉘에서 실행되도록 함수를 변경하면 extglob에 대한 현재 쉘 옵션을 검사하는 것에 대해 걱정할 필요가 없으며, 현재 쉘에 영향을주지 않고 설정할 수 있습니다. 이것은 기능을 엄청나게 단순화시킵니다. 또한 위치 매개 변수를 "제자리에"업데이트하므로 로컬 변수가 필요하지 않습니다.

trim() {
    shopt -s extglob
    set -- "${1##+([[:space:]])}"
    printf "%s" "${1%%+([[:space:]])}" 
}

그래서:

$ s=$'\t\n \r\tfoo  '
$ shopt -u extglob
$ shopt extglob
extglob         off
$ printf ">%q<\n" "$s" "$(trim "$s")"
>$'\t\n \r\tfoo  '<
>foo<
$ shopt extglob
extglob         off

2
당신이 관찰 한 것처럼 trim ()은 앞뒤 공백 만 제거합니다.
GuruM

mkelement가 이미 언급했듯이 $ (trim $ string) 대신 인용 된 문자열 즉 $ (trim "$ string")로 함수 매개 변수를 전달해야합니다. 올바른 사용법을 보여주기 위해 코드를 업데이트했습니다. 감사.
GuruM

내가 쉘 옵션에 대해 알고 감사만큼, 나는 최종 결과는 단순히 2 패턴 대체하는 것보다 더 우아한라고 생각하지 않습니다
sehe을

(최근 충분한 버전의 Bash?) extglob를 사용하면 shopt -p다음 을 사용하여 옵션을 복원하는 메커니즘을 단순화 할 수 있습니다 . 단순히 local restore="$(shopt -p extglob)" ; shopt -s extglob함수의 시작 부분과 eval "$restore"끝에 작성하십시오 (허용 된 eval은 제외하고 ...).
Maëlan

훌륭한 솔루션! 하나 개의 잠재적 인 개선 : 그것은처럼 보이는 [[:space:]]공간, 음,로 대체 할 수있다 : ${var##+( )}${var%%+( )}뿐만 아니라 직장과 그들이 쉽게 읽을 수 있습니다.
DKroot

40

다음과 echo같이 간단하게 다듬을 수 있습니다 .

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

이것은 여러 개의 연속 된 공간을 하나로 축소합니다.
Evgeni Sergeev

7
foo와일드 카드 가 포함되어 있을 때 시도 했습니까 ? 예를 들어 foo=" I * have a wild card"... 놀람! 또한 이것은 인접한 여러 공간을 하나로 축소합니다.
gniourf_gniourf

5
이것은 다음과 같은 경우에 탁월한 솔루션입니다. 1. 끝에 공백이 필요하지 않습니다. 2. 각 단어 사이에 공백이 하나만 필요합니다. 3. 와일드 카드없이 제어 된 입력으로 작업하고 있습니다. 본질적으로 형식이 잘못된 목록을 좋은 목록으로 바꿉니다.
musicin3d 2016

와일드 카드 @gniourf_gniourf +1에 대한 좋은 알림입니다. 여전히 탁월한 솔루션 인 Vamp. 당신도 +1합니다.
Dr Beco

25

난 항상 sed로 해왔 어

  var=`hg st -R "$path" | sed -e 's/  *$//'`

더 우아한 솔루션이 있다면 누군가가 게시하기를 바랍니다.


구문을 설명해 주 sed시겠습니까?
farid99

2
정규식은 모든 후행 공백과 일치하고 아무것도없는 것으로 대체합니다.
Paul Tomblin

4
공백 문자는 어떻습니까?
키안 첸

이것은 모든 후행 공백을 제거합니다 sed -e 's/\s*$//'. 설명 : 's'는 검색을 의미하고 '\ s'는 모든 공백을 의미하고 '*'는 0 또는 다수를 의미하며 '$'는 줄이 끝날 때까지를 의미하며 '//'는 모든 일치 항목을 빈 문자열로 대체 함을 의미합니다. .
Craig

's / * $ //'에서 왜 단일 공백 ​​대신 별표 앞에 2 개의 공백이 있습니까? 오타입니까?
Brent212

24

다음을 사용하여 줄 바꿈을 삭제할 수 있습니다 tr.

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done

8
문자열 중간에서 '\ n'을 제거하고 싶지 않으며 처음이나 끝에서만 제거하고 싶습니다.
너무 많은 PHP

24

Bash의 확장 패턴 일치 기능을 활성화하면 ( shopt -s extglob) 다음을 사용할 수 있습니다.

{trimmed##*( )}

임의의 선행 공백을 제거합니다.


훌륭한! 이것이 가장 가볍고 우아한 솔루션이라고 생각합니다.
dubiousjim

1
(a) 모든 형태의 공백을 처리하고 (b) 후행 공백을 처리 하는 유사하지만보다 일반적인 해결책은 아래 @GuruM의 게시물을 참조하십시오 .
mklement0

코드 스 니펫을 함수로 다시 작성하는 데 문제가있는 @mkelement +1 감사합니다
GuruM

OpenBSD의 기본 / bin / ksh에서도 작동합니다. /bin/sh -o posix작동하지만 의심 스럽습니다.
클린트 Pachl

여기 배쉬 마법사가 아닙니다. 뭐야 trimmed? 내장 된 것입니까, 자르고있는 변수입니까?
Abhijit Sarkar

19
# Trim whitespace from both ends of specified parameter

trim () {
    read -rd '' $1 <<<"${!1}"
}

# Unit test for trim()

test_trim () {
    local foo="$1"
    trim foo
    test "$foo" = "$2"
}

test_trim hey hey &&
test_trim '  hey' hey &&
test_trim 'ho  ' ho &&
test_trim 'hey ho' 'hey ho' &&
test_trim '  hey  ho  ' 'hey  ho' &&
test_trim $'\n\n\t hey\n\t ho \t\n' $'hey\n\t ho' &&
test_trim $'\n' '' &&
test_trim '\n' '\n' &&
echo passed

2
놀랄 만한! 간단하고 효과적입니다! 분명히 내가 가장 좋아하는 솔루션. 감사합니다!
xebeche

1
@CraigMcQueen read변수 이름으로 $ 1라는 이름으로 변수에 저장 될 것입니다. $ {! 1}
Aquarius Power

2
trim () 함수의 매개 변수는 변수 이름입니다. test_trim () 내부의 trim () 호출을 참조하십시오. test_trim ()에서 호출 된 trim () 내에서 $ 1은 foo로 확장되고 $ {! 1}은 $ foo (즉, 변수 foo의 현재 내용)로 확장됩니다. bash 매뉴얼에서 'variable indirection'을 검색하십시오.
flabdablet

1
한 번의 호출로 여러 변수를 다듬을 수 있도록이 작은 수정은 어떻습니까? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
유전자 Pavlovsky

2
@AquariusPower 하나의 라이너 버전을 위해 서브 쉘에서 에코를 사용할 필요 read -rd '' str <<<"$str"가 없습니다.
flabdablet

12

많은 답변이 있지만 여전히 다음과 같은 이유로 방금 작성한 스크립트를 언급 할 가치가 있다고 생각합니다.

  • 쉘 bash / dash / busybox 쉘에서 성공적으로 테스트되었습니다.
  • 매우 작습니다
  • 외부 명령에 의존하지 않으며 분기 할 필요가 없습니다 (-> 빠르고 낮은 리소스 사용량)
  • 예상대로 작동합니다.
    • 그것은 모든 공간과 탭을 시작과 끝에서 제거하지만 더 이상은 제거하지 않습니다.
    • 중요 : 문자열 중간에서 아무것도 제거하지 않으며 (많은 다른 답변이 수행), 줄 바꿈조차 남아 있습니다.
    • special : "$*"하나의 공백을 사용하여 여러 인수를 조인합니다. 첫 번째 인수 만 자르고 출력하려면 "$1"대신 사용하십시오.
    • 일치하는 파일 이름 패턴 등에 문제가없는 경우

스크립트 :

trim() {
  local s2 s="$*"
  until s2="${s#[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

용법:

mystring="   here     is
    something    "
mystring=$(trim "$mystring")
echo ">$mystring<"

산출:

>here     is
    something<

C의 Bah는 구현하기가 더 간단합니다!
Nils

확실한. 불행하게도,이 C하지 그리고 가끔은 외부 도구를 호출 피하려고
다니엘 알더

코드를 더 읽기 쉽고 복사 할 수 있도록하기 위해 대괄호를 이스케이프 문자로 변경할 수 있습니다.[\ \t]
leondepeon

@leondepeon 이것을 시도 했습니까? 나는 그것을 쓰고 다시 시도했을 때 시도했지만, 당신의 제안은 bash, dash, busybox에서 작동하지 않습니다
Daniel Alder

@DanielAlder 내가했지만 이미 3 년 전에 사용했던 코드를 찾을 수 없습니다. 그러나 이제는 [[:space:]]다른 답변 중 하나와 같이 사용 합니다. stackoverflow.com/a/3352015/3968618
leondepeon

11

old-school을 사용할 수 있습니다 tr. 예를 들어, 이것은 공백이 제거 된 git 저장소의 수정 된 파일 수를 리턴합니다.

MYVAR=`git ls-files -m|wc -l|tr -d ' '`

1
이것은 앞뒤 공백을 자르지 않으며 문자열에서 공백을 모두 제거합니다.
Nick

11

이것은 나를 위해 일했다 :

text="   trim my edges    "

trimmed=$text
trimmed=${trimmed##+( )} #Remove longest matching series of spaces from the front
trimmed=${trimmed%%+( )} #Remove longest matching series of spaces from the back

echo "<$trimmed>" #Adding angle braces just to make it easier to confirm that all spaces are removed

#Result
<trim my edges>

같은 결과를 위해 더 적은 수의 줄에 넣으려면 :

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}

1
나를 위해 작동하지 않았다. 첫 번째는 트리밍되지 않은 문자열을 인쇄했습니다. 두 번째는 나쁜 대체를 던졌습니다. 여기서 무슨 일이 일어나고 있는지 설명해 주시겠습니까?
musicin3d 2016

1
@ musicin3d : 이것은 내가 자주 사용하는 사이트 로 자세한 내용 은 bash 검색 에서 변수 조작이 어떻게 작동하는지${var##Pattern} 설명합니다. 또한이 사이트에서는 bash 패턴에 대해 설명합니다 . 따라서 ##수단은 주어진 패턴을 앞면에서 %%제거 하고 수단은 주어진 패턴을 뒷면에서 제거합니다. +( )부분 패턴이며 수단 "하나 또는 그 이상의 공간의 발생"
gMale

재밌는 것은 프롬프트에서 작동했지만 bash 스크립트 파일로 바꾼 후에는 작동하지 않습니다.
Dr Beco

기묘한. 두 경우 모두 동일한 bash 버전입니까?
gMale

11
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

또는

# Strip leading white space (new line inclusive).
ltrim(){
    [[ "$1" =~ [^[:space:]].* ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    [[ "$1" =~ .*[^[:space:]] ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

또는

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

또는

# Strip leading specified characters.  ex: str=$(ltrim "$str" $'\n a')
ltrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"]) ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip trailing specified characters.  ex: str=$(rtrim "$str" $'\n a')
rtrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1" "$2")" "$2")"
}

또는

moskit의 expr soulution을 기반으로 ...

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$"`"
}

또는

# Strip leading white space (new line inclusive).
ltrim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)"`"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    printf "%s" "`expr "$1" : "^\(.*[^[:space:]]\)[[:space:]]*$"`"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

8

스크립트가 변수 할당을 사용하여 작업을 수행하는 것을 보았습니다.

$ xyz=`echo -e 'foo \n bar'`
$ echo $xyz
foo bar

공백은 자동으로 합쳐지고 잘립니다. 쉘 메타 문자 (잠재적 주입 위험)에주의해야합니다.

쉘 조건부에서 항상 큰 따옴표로 묶인 변수 대체를 권장합니다.

if [ -n "$var" ]; then

변수의 -o 또는 다른 내용과 같은 것이 테스트 인수를 수정할 수 있기 때문입니다.


3
그것은의 인용 부호가 사용하는 것입니다 $xyz으로 echo그 공백의 유착, 수행 하지 변수 할당을. 예제에서 트림 된 값을 변수에 저장하려면을 사용해야 xyz=$(echo -n $xyz)합니다. 또한이 방법은 잠재적으로 원치 않는 경로 이름 확장 (글 로빙)을 따릅니다.
mklement0

이것은 잘못입니다. xyz변수 의 값 은 잘리지 않습니다.
시저 솔

7
var='   a b c   '
trimmed=$(echo $var)

1
두 단어 사이에 공백이 둘 이상 있으면 작동하지 않습니다. 시도하십시오 echo $(echo "1 2 3")(1, 2 및 3 사이에 두 개의 공백이 있음).
joshlf

7

나는 단순히 sed를 사용합니다 :

function trim
{
    echo "$1" | sed -n '1h;1!H;${;g;s/^[ \t]*//g;s/[ \t]*$//g;p;}'
}

a) 한 줄짜리 문자열에서의 사용 예

string='    wordA wordB  wordC   wordD    '
trimmed=$( trim "$string" )

echo "GIVEN STRING: |$string|"
echo "TRIMMED STRING: |$trimmed|"

산출:

GIVEN STRING: |    wordA wordB  wordC   wordD    |
TRIMMED STRING: |wordA wordB  wordC   wordD|

b) 여러 줄 문자열에서의 사용 예

string='    wordA
   >wordB<
wordC    '
trimmed=$( trim "$string" )

echo -e "GIVEN STRING: |$string|\n"
echo "TRIMMED STRING: |$trimmed|"

산출:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) 최종 참고 사항 :
함수를 사용하지 않으려면 단일 행 문자열에 대해 다음과 같이 "쉽게 기억하기"명령을 사용할 수 있습니다.

echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

예:

echo "   wordA wordB wordC   " | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

산출:

wordA wordB wordC

위의 여러 줄 문자열을 사용하면 효과가 있지만 GuruM이 주석에서 알 수 있듯이 후행 / 선행 내부 다중 공간도 줄어 듭니다.

string='    wordAA
    >four spaces before<
 >one space before<    '
echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

산출:

wordAA
>four spaces before<
>one space before<

따라서 그 공간을 유지하려면 내 대답의 시작 부분에 기능을 사용하십시오!

d) 함수 트림 내부에서 사용되는 여러 줄 문자열에서 sed 구문 "find and replace"에 대한 설명 :

sed -n '
# If the first line, copy the pattern to the hold buffer
1h
# If not the first line, then append the pattern to the hold buffer
1!H
# If the last line then ...
$ {
    # Copy from the hold to the pattern buffer
    g
    # Do the search and replace
    s/^[ \t]*//g
    s/[ \t]*$//g
    # print
    p
}'

참고 : @mkelement가 제안한 것처럼 한 줄 문자열에서는 작동하지만 여러 줄 문자열에서는 작동하지 않습니다.
GuruM

1
당신은 잘못되었습니다 : 그것은 여러 줄 문자열에서도 작동합니다. 그냥 테스트 해보세요! :)
Luca Borrione

사용법에 +1하면 코드를 쉽게 테스트 할 수 있습니다. 그러나 코드는 여전히 여러 줄 문자열에서 작동하지 않습니다. 출력을주의 깊게 살펴보면 선행 / 후행 내부 공간도 제거됩니다. 예를 들어 "멀티 라인"앞의 공간이 "멀티 라인"으로 바뀝니다. 각 줄마다 앞 / 뒤 공간을 늘리십시오.
GuruM

이제 무슨 말인지 알 겠어요! 답장을 보내 주셔서 감사합니다. 제 답변을 수정했습니다.
Luca Borrione

@ "Luca Borrione"-welcome :-) trim ()에서 사용하는 sed 구문을 설명 하시겠습니까? 또한 코드 사용자가 다른 용도로 코드를 수정하는 데 도움이 될 수 있습니다. 또한 정규 표현식의 경우를 찾는 데 도움이 될 수도 있습니다.
GuruM

6

다음은 공백을 자르고 정규화하는 trim () 함수입니다.

#!/bin/bash
function trim {
    echo $*
}

echo "'$(trim "  one   two    three  ")'"
# 'one two three'

정규 표현식을 사용하는 또 다른 변형입니다.

#!/bin/bash
function trim {
    local trimmed="$@"
    if [[ "$trimmed" =~ " *([^ ].*[^ ]) *" ]]
    then 
        trimmed=${BASH_REMATCH[1]}
    fi
    echo "$trimmed"
}

echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'

첫 번째 접근 방식은 내부 공백을 정규화 할뿐만 아니라 공백의 모든 내부 범위를 각각 단일 공백으로 대체하는 것뿐만 아니라 *입력 문자열 의 문자와 같이 글 로빙 (경로 이름 확장)을 받기 쉽다는 점에서 까다 롭습니다. 현재 작업 폴더의 모든 파일 및 폴더로 확장하십시오. 마지막으로 $ IFS가 기본값이 아닌 값으로 설정되면 트리밍이 작동하지 않을 수 있습니다 (추가하면 쉽게 해결할 수 있음 local IFS=$' \t\n'). 트리밍은 공백 \t\n문자 형식의 공백으로 제한됩니다 .
mklement0 2016 년

1
두 번째 정규 표현식 기반 접근 방식은 훌륭하고 부작용이 없지만 현재 형태에는 문제가 있습니다. 작동하고 (b) 정규 표현식 자체는 공백으로 둘러싸인 단일 비 공백 문자 인 입력 문자열을 처리하지 않습니다. 이러한 문제를 해결하려면 if줄을 다음과 같이 바꾸십시오 if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]. 마지막으로, 접근 방식은 다른 형태의 공백이 아닌 공백 만 처리합니다 (다음 주석 참조).
mklement0 2016 년

2
일반 expresssions을 활용 기능 만 다루는 공간 과 공백이 아닌 다른 형태의,하지만 일반화하는 것은 쉽다 : 바꾸기 if와 라인 :[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0

6

AWK 사용 :

echo $var | awk '{gsub(/^ +| +$/,"")}1'

작동하는 것처럼 보이는 달콤한 (ex :) $stripped_version=echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1'``
rogerdpack

4
awk를 제외하고는 아무것도하지 않습니다 : 따옴표없는 변수의 echo '는 이미 공백을 제거했습니다.
glenn jackman

6

할당은 선행 및 후행 공백을 무시하므로 다음과 같이 트리밍 할 수 있습니다.

$ var=`echo '   hello'`; echo $var
hello

8
그건 사실이 아니야. 할당이 아닌 공백을 제거하는 것은 "에코"입니다. 예를 들어, echo "$var"공백이있는 값을 확인하십시오.
Nicholas Sushkin

2
@NicholasSushkin One은 할 수 var=$(echo $var)있지만 권장하지 않습니다. 여기에 제시된 다른 솔루션이 선호됩니다.
xebeche

5

이것은 원치 않는 글 로빙에 문제가 없으며 내부 공백은 수정되지 않습니다 ( $IFS기본값으로 설정되어 있다고 가정 ' \t\n').

첫 번째 줄 바꿈 (포함하지 않음) 또는 문자열 끝 중 먼저 오는 것을 읽은 다음 선행 및 후행 공백과 \t문자의 혼합을 제거 합니다. 여러 줄을 유지하고 앞뒤 줄 바꿈을 제거하려면 read -r -d '' var << eof대신 사용하십시오. 그러나 입력에 포함되는 경우 \neof바로 전에 차단됩니다. (즉, 공백, 다른 형태의 \r, \f그리고 \v,있다 없다 당신이 $ IFS에 추가 할 경우에도 제거.)

read -r var << eof
$var
eof


5

이렇게하면 문자열에서 모든 공백이 제거됩니다.

 VAR2="${VAR2//[[:space:]]/}"

///문자열에서 첫 번째 발생 및 모든 공백 발생을 대체합니다 . 즉, 모든 공백은 다음으로 대체됩니다.


4

이것은 내가 본 가장 간단한 방법입니다. Bash 만 사용하고 몇 줄에 불과하며 정규 표현식은 간단하며 모든 형태의 공백과 일치합니다.

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

테스트 할 샘플 스크립트는 다음과 같습니다.

test=$(echo -e "\n \t Spaces and tabs and newlines be gone! \t  \n ")

echo "Let's see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let's fix that..."

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

echo
echo "----------"
echo -e "Testing:${test}:Tested"  # "Testing:Spaces and tabs and newlines be gone!"
echo "----------"
echo
echo "Ah, much better."

1
예를 들어 (ye gods!), 파이썬으로 포격하는 것이 반드시 바람직합니다. 공백 만 포함 된 문자열을 올바르게 처리하는 것이 더 간단하고 더 일반적이라고 생각하는 것을 제외하고는 약간 단순화 된 표현은 다음과 같습니다.^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
Ron Burk

4

파이썬은 strip()PHP와 동일하게 작동 하는 기능 을 가지고 trim()있으므로 약간의 인라인 파이썬을 통해 이해하기 쉬운 유틸리티를 만들 수 있습니다.

alias trim='python -c "import sys; sys.stdout.write(sys.stdin.read().strip())"'

이렇게하면 선행 및 후행 공백이 줄어 듭니다 (개행 포함).

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi

그것이 작동하는 동안 문자열을 자르기 위해 완전한 파이썬 인터프리터를 시작하지 않는 솔루션을 제공하는 것이 좋습니다. 그냥 낭비입니다.
pdwalker

3
#!/bin/bash

function trim
{
    typeset trimVar
    eval trimVar="\${$1}"
    read trimVar << EOTtrim
    $trimVar
EOTtrim
    eval $1=\$trimVar
}

# Note that the parameter to the function is the NAME of the variable to trim, 
# not the variable contents.  However, the contents are trimmed.


# Example of use:
while read aLine
do
    trim aline
    echo "[${aline}]"
done < info.txt



# File info.txt contents:
# ------------------------------
# ok  hello there    $
#    another  line   here     $
#and yet another   $
#  only at the front$
#$



# Output:
#[ok  hello there]
#[another  line   here]
#[and yet another]
#[only at the front]
#[]

3

sdiff정리하기 위해 지저분한 출력에서 일부 코드를 추가해야한다는 것을 알았 습니다.

sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt 
sed -n 1'p' c12diff.txt | sed 's/ *$//g' | tr -d '\n' | tr -d '\t'

이렇게하면 후행 공백과 다른 보이지 않는 문자가 제거됩니다.


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