Como aparar espaços em branco a partir de uma variável Bash?

922

Eu tenho um script de shell com este código:

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

Mas o código condicional sempre é executado, porque hg stsempre imprime pelo menos um caractere de nova linha.

  • Existe uma maneira simples de remover o espaço em branco $var(como trim()no PHP )?

ou

  • Existe uma maneira padrão de lidar com esse problema?

Eu poderia usar sed ou AWK , mas gostaria de pensar que há uma solução mais elegante para esse problema.

demais php
fonte
3
Relacionado, se você deseja aparar espaço em um número inteiro e apenas obtê-lo, envolva com $ (($ var)) e pode até fazer isso quando estiver entre aspas duplas. Isso se tornou importante quando usei a declaração de data e com os nomes de arquivos.
Volomike
"Existe uma maneira padrão de lidar com esse problema?" Sim, use [[em vez de [. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly
Se ajudar, pelo menos onde estou testando no Ubuntu 16.04. Usando as seguintes partidas cortar em todos os sentidos: echo " This is a string of char " | xargs. Se você no entanto, ter uma única citação no texto que você pode fazer o seguinte: echo " This i's a string of char " | xargs -0. Note que eu menciono mais recente de xargs (4.6.0)
Luis Alvarado
A condição não é verdadeira devido a uma nova linha, pois os backticks engolem a última nova linha. Isso não imprimirá nada test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi, no entanto, será test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi- portanto, deve haver mais do que apenas uma nova linha no final.
Mecki
A = "123 4 5 6"; B = echo $A | sed -r 's/( )+//g';
Bruziuz 17/07/2018

Respostas:

1022

Vamos definir uma variável que contenha espaços em branco iniciais, finais e intermediários:

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

Como remover todo o espaço em branco (indicado por [:space:]in 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

Como remover apenas os espaços em branco à esquerda:

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

Como remover apenas o espaço em branco à direita:

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

Como remover os espaços à esquerda e à direita - encadeie os seds:

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

Como alternativa, se o seu bash suportar, você poderá substituí-lo echo -e "${FOO}" | sed ...por sed ... <<<${FOO}, desta forma (para espaços em branco à direita):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"
MattyV
fonte
63
Para generalizar a solução para lidar com todas as formas de espaço em branco, substitua o caractere de espaço nos comandos tre sedpor [[:space:]]. Observe que a sedabordagem funcionará apenas na entrada de linha única . Para abordagens que funcionam com entrada de várias linhas e também usam os recursos internos do bash, consulte as respostas de @bashfu e @GuruM. Uma versão generalizada, inline da solução da @Nicholas Sushkin ficaria assim: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0
7
Se você fizer isso com frequência, anexar alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"ao seu ~/.profilepermite que você use echo $SOMEVAR | trime cat somefile | trim.
instanceof me
Eu escrevi uma sedsolução que usa apenas uma única expressão ao invés de dois: sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. Ele apara os espaços em branco à esquerda e à esquerda e captura todas as seqüências separadas por espaços em branco de caracteres que não sejam espaços em branco no meio. Aproveitar!
Victor Zamanian
@VictorZamanian Sua solução não funcionará se a entrada contiver apenas espaço em branco. As soluções sed de dois padrões fornecidas por MattyV e instância de mim funcionam bem com apenas entrada de espaço em branco.
Torben
@Torben Fair point. Suponho que a expressão única possa ser condicionada, com |, de modo a mantê-la como uma expressão única, em vez de várias.
Victor Zamanian
966

Uma resposta simples é:

echo "   lol  " | xargs

Xargs fará o corte para você. É um comando / programa, sem parâmetros, retorna a string aparada, fácil assim!

Nota: isso não remove todos os espaços internos, portanto "foo bar"permanece o mesmo; NÃO se torna "foobar". No entanto, vários espaços serão condensados ​​em espaços únicos, "foo bar"tornando-se assim "foo bar". Além disso, ele não remove os caracteres de final de linha.

makevoid
fonte
27
Agradável. Isso funciona muito bem. Decidi canalizá-lo xargs echoapenas para ser detalhado sobre o que estou fazendo, mas o xargs por si só usará o eco por padrão.
Vai
24
Bom truque, mas tenha cuidado, você pode usá-lo para cadeias de caracteres de uma linha, mas - pelo xargs design - ele não fará apenas ajustes com o conteúdo canalizado de várias linhas. sed é seu amigo então.
Jocelyn delalande
22
O único problema com o xargs é que ele apresentará uma nova linha; se você quiser mantê-la, eu recomendaria sed 's/ *$//'como alternativa. Você pode ver a xargsnova linha como esta: echo -n "hey thiss " | xargs | hexdump você vai notar 0a73o aé a nova linha. Se você fizer o mesmo com sed: echo -n "hey thiss " | sed 's/ *$//' | hexdumpverá 0073, nenhuma nova linha.
8
Cuidado; isso será difícil se a string para xargs contiver espaços excedentes no meio. Como "este é um argumento". xargs se dividirá em quatro.
bos
64
Isto é mau. 1. Ele vai se transformar a<space><space>bem a<space>b. 2. Ainda mais: ele vai se transformar a"b"c'd'eem abcde. 3. Ainda mais: falhará a"betc.
Sasha
359

Existe uma solução que usa apenas os built-ins do Bash chamados curingas :

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

Aqui está o mesmo envolvido em uma função:

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

Você passa a string para ser cortada na forma de aspas. por exemplo:

trim "   abc   "

Uma coisa boa dessa solução é que ela funcionará com qualquer shell compatível com POSIX.

Referência

Mateusz Piotrowski
fonte
18
Inteligente! Esta é a minha solução favorita, pois usa a funcionalidade bash integrada. Obrigado por publicar! @ San, são duas guarnições de cadeia aninhadas. Por exemplo, s=" 1 2 3 "; echo \""${s%1 2 3 }"\"apararia tudo do final, retornando a liderança " ". Subbing 1 2 3 with [![:space:]]*diz a ele para "encontrar o primeiro caractere que não seja o espaço, depois o derrube e tudo mais". Usar em %%vez de %torna a operação de recorte do final gananciosa. Isso é aninhado em um corte não ganancioso desde o início; portanto, você efetua " "o corte desde o início. Em seguida, troque%, # e * pelos espaços finais. Bam!
Mark G.
2
Não encontrei efeitos colaterais indesejados, e o código principal também funciona com outros shells do tipo POSIX. No entanto, no Solaris 10, ele não funciona /bin/sh(apenas com /usr/xpg4/bin/sh, mas não é isso que será usado nos scripts sh comuns).
vinc17
9
Solução muito melhor do que usar sed, tr etc., pois é muito mais rápido, evitando qualquer garfo (). Em Cygwin, a diferença de velocidade é de magnitude.
Gene Pavlovsky
9
@ San No começo, fiquei perplexo porque pensei que eram expressões regulares. Eles não são. Em vez disso, é a sintaxe da correspondência de padrões ( gnu.org/software/bash/manual/html_node/Pattern-Matching.html , wiki.bash-hackers.org/syntax/pattern ) usada na Remoção de Substring ( tldp.org/LDP/abs /html/string-manipulation.html ). É o que ${var%%[![:space:]]*}diz "remova de varsua substring mais longa que começa com um caractere não espacial". Isso significa que você fica apenas com o (s) espaço (s) inicial (is), com o qual você remove posteriormente ${var#... A linha a seguir (à direita) é o oposto.
Ohad Schneider
8
Esta é predominantemente a solução ideal. Bifurcação um ou mais externos processos (por exemplo, awk, sed, tr, xargs) apenas para os espaços em branco da guarnição de uma única corda é fundamentalmente insano - especialmente quando a maioria das conchas (incluindo bash) fornecem seqüência nativa munging instalações out-of-the-box.
22918 Cecil Curry
81

O Bash possui um recurso chamado expansão de parâmetro , que, entre outras coisas, permite a substituição de cadeias com base nos chamados padrões (os padrões lembram expressões regulares, mas existem diferenças e limitações fundamentais). [linha original do flussence: Bash tem expressões regulares, mas estão bem escondidas:]

A seguir, demonstramos como remover todo o espaço em branco (mesmo do interior) de um valor variável.

$ 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
user42092
fonte
2
Ou melhor, funciona para espaços no meio de uma var, mas não quando tento ancorá-la no final.
Paul Tomblin
Isso ajuda alguma? Na página de manual: "$ {parameter / pattern / string} [...] Se o padrão começar com%, ele deverá corresponder ao final do valor expandido do parâmetro."
@ Ant, então elas não são expressões regulares, mas algo parecido?
Paul Tomblin
3
Eles são regex, apenas um dialeto estranho.
13
${var/ /}remove o primeiro caractere de espaço. ${var// /}remove todos os caracteres de espaço. Não há como aparar apenas espaços em branco à esquerda e à esquerda com apenas essa construção.
Gilles 'SO- stop be evil'
60

Para remover todos os espaços do começo e do fim de uma string (incluindo caracteres de fim de linha):

echo $variable | xargs echo -n

Isso removerá espaços duplicados também:

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

Produz: 'essa string tem muitos espaços'

rkachach
fonte
5
Basicamente, o xargs remove todos os delimitadores da string. Por padrão, ele usa o espaço como delimitador (isso pode ser alterado pela opção -d).
Rkachach 8/03
4
Essa é de longe a solução mais limpa (curta e legível).
Potherca
Por que você precisa echo -nmesmo? echo " my string " | xargstem a mesma saída.
bfontaine
eco -n remove o fim da linha, bem
rkachach
55

Tira um espaço à esquerda e um à direita

trim()
{
    local trimmed="$1"

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

    echo "$trimmed"
}

Por exemplo:

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

Resultado:

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

Tira todos os espaços à esquerda e à direita

trim()
{
    local trimmed="$1"

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

    echo "$trimmed"
}

Por exemplo:

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

Resultado:

'two leading', 'two trailing', 'two leading and two trailing'
wjandrea
fonte
9
Isso irá aparar apenas 1 caractere de espaço. Assim, o eco resulta em'hello world ', 'foo bar', 'both sides '
Joe
@ Joe eu adicionei uma opção melhor.
Wjandrea
42

Na seção Bash Guide sobre globbing

Para usar um extglob em uma expansão de parâmetro

 #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  

Aqui está a mesma funcionalidade agrupada em uma função (NOTA: É necessário citar a string de entrada passada para a função):

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.
}

Uso:

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

Se alterarmos a função a ser executada em um subshell, não precisamos nos preocupar em examinar a opção atual do shell para extglob, podemos apenas configurá-lo sem afetar o shell atual. Isso simplifica tremendamente a função. Também atualizo os parâmetros posicionais "no local" para não precisar nem de uma variável local

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

tão:

$ 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
GuruM
fonte
2
como você observou trim () remove somente os espaços em branco à esquerda e à direita.
GuruM 12/12/12
Como o mkelement já notou, você precisa passar o parâmetro de função como uma string entre aspas, ou seja, $ (trim "$ string") em vez de $ (trim $ string). Atualizei o código para mostrar o uso correto. Obrigado.
GuruM
Por mais que eu apreciam sabendo sobre as opções de shell, eu não acho que o resultado final é mais elegante do que simplesmente fazer 2 substituições padrão
sehe
Observe que (com uma versão recente o suficiente do Bash?), Você pode simplificar o mecanismo para restaurar a opção extglob, usando shopt -p: basta escrever local restore="$(shopt -p extglob)" ; shopt -s extglobno início de sua função e eval "$restore"no final (exceto, se concedido, eval é mau…).
Maëlan 24/03/19
Ótima solução! Uma melhoria potencial: parece que [[:space:]]poderia ser substituído por, bem, um espaço: ${var##+( )}e ${var%%+( )}trabalho, bem e eles são mais fáceis de ler.
DKroot
40

Você pode cortar simplesmente com echo:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'
VAmp
fonte
Isso recolhe vários espaços contíguos em um.
Evgeni Sergeev
7
Você tentou quando foo contém um curinga? por exemplo foo=" I * have a wild card"... surpresa! Além disso, isso reduz vários espaços contíguos em um.
gniourf_gniourf
5
Esta é uma excelente solução se você: 1. não deseja espaços nas extremidades 2. deseja apenas um espaço entre cada palavra 3. está trabalhando com uma entrada controlada sem caracteres curinga. Essencialmente, transforma uma lista mal formatada em uma boa.
musicin3d
Bom lembrete dos caracteres curinga @gniourf_gniourf +1. Ainda é uma excelente solução, Vamp. +1 para você também.
Dr Beco
25

Eu sempre fiz isso com sed

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

Se houver uma solução mais elegante, espero que alguém a publique.

Paul Tomblin
fonte
você poderia explicar a sintaxe para sed?
precisa saber é
2
A expressão regular corresponde a todos os espaços em branco à direita e a substitui por nada.
Paul Tomblin
4
Que tal espaços em branco à esquerda?
Qian Chen
Isso remove todo o espaço em branco à direita sed -e 's/\s*$//'. Explicação: 's' significa pesquisa, o '\ s' significa todos os espaços em branco, o '*' significa zero ou muitos, o '$' significa até o final da linha e '//' significa substitui todas as correspondências por uma sequência vazia .
Craig
Em 's / * $ //', por que existem 2 espaços antes do asterisco, em vez de um único espaço? Isso é um erro de digitação?
Brent212 19/03
24

Você pode excluir novas linhas com tr:

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done
Adam Rosenfield
fonte
8
Não quero remover '\ n' do meio da string, apenas do começo ou do fim.
php too much
24

Com os recursos de correspondência estendida de padrões do Bash ativados ( shopt -s extglob), você pode usar este:

{trimmed##*( )}

para remover uma quantidade arbitrária de espaços à esquerda.

Mooshu
fonte
Formidável! Eu acho que essa é a solução mais leve e elegante.
precisa saber é o seguinte
1
Consulte a publicação do @ GuruM abaixo para obter uma solução semelhante, porém mais genérica, que (a) lida com todas as formas de espaço em branco e (b) também lide com o espaço em branco à direita .
usar o seguinte comando
@mkelement +1 por reescrever meu snippet de código como uma função. Obrigado
GuruM
Também funciona com o padrão / bin / ksh do OpenBSD. /bin/sh -o posixfunciona também, mas desconfio.
Clint Pachl
Não é um assistente de festa aqui; o que é trimmed? É uma coisa embutida ou a variável que está sendo cortada?
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
flabdablet
fonte
2
Surpreendente! Simples e eficaz! Claramente minha solução favorita. Obrigado!
Xebeche 02/08/2012
1
@CraigMcQueen é o valor da variável, pois readarmazenará na variável pelo seu nome $ 1 uma versão reduzida de seu valor $ {! 1}
Aquarius Power
2
O parâmetro da função trim () é um nome de variável: consulte a chamada para trim () dentro de test_trim (). Dentro de trim () como chamado de test_trim (), $ 1 se expande para foo e $ {! 1} se expande para $ foo (ou seja, para o conteúdo atual da variável foo). Pesquise o manual do bash por 'variável indireta'.
Flabdablet # 1
1
Que tal essa pequena modificação, para suportar o corte de vários vars em uma chamada? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
Gene Pavlovsky
2
@AquariusPower, não há necessidade de usar eco em um subshell para a versão one-liner, apenas o read -rd '' str <<<"$str"fará.
Flabdablet
12

Existem muitas respostas, mas ainda acredito que vale a pena mencionar meu roteiro recém-escrito porque:

  • foi testado com sucesso no shell bash / dash / busybox dos shells
  • é extremamente pequeno
  • não depende de comandos externos e não precisa de bifurcar (-> uso rápido e baixo de recursos)
  • funciona como esperado:
    • retira todos os espaços e guias do começo e do fim, mas não mais
    • importante: não remove nada do meio da string (muitas outras respostas o fazem), até as novas linhas permanecem
    • especial: o "$*"junta vários argumentos usando um espaço. se você deseja aparar e produzir apenas o primeiro argumento, use"$1" em vez
    • se não tiver nenhum problema com os padrões de nome de arquivo correspondentes, etc.

O script:

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"
}

Uso:

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

Resultado:

>here     is
    something<
Daniel Alder
fonte
Bah em C isso seria muito mais simples de implementar!
Nils
Certo. Infelizmente, isso não é C e, às vezes, você deseja evitar chamar ferramentas externas #
Daniel Alder
Para tornar o código mais legível e compatível com cópia e cópia, você pode alterar os colchetes para caracteres de escape:[\ \t]
leondepeon
@leondepeon você tentou isso? Eu tentei quando o escrevi e tentei novamente, e sua sugestão não funciona em nada de bash, dash, busybox
Daniel Alder
@DanielAlder eu fiz, mas como já faz 3 anos, não consigo encontrar o código em que o usei. Agora, no entanto, eu provavelmente usaria [[:space:]]como em uma das outras respostas: stackoverflow.com/a/3352015/3968618
leondepeon
11

Você pode usar a velha escola tr. Por exemplo, isso retorna o número de arquivos modificados em um repositório git, espaços em branco removidos.

MYVAR=`git ls-files -m|wc -l|tr -d ' '`
pojo
fonte
1
Isso não corta o espaço em branco da frente e de trás - remove todo o espaço em branco da string.
Nick
11

Isso funcionou para mim:

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>

Para colocar isso em menos linhas para o mesmo resultado:

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}
gmale
fonte
1
Não funcionou para mim. O primeiro imprimiu uma corda não aparada. O segundo lançou uma substituição ruim. Você pode explicar o que está acontecendo aqui?
musicin3d
1
@ musicin3d: este é um site que eu uso com frequência que explica como a manipulação de variáveis ​​funciona na busca do bash para ${var##Pattern}obter mais detalhes. Além disso, este site explica os padrões do bash . Portanto, os ##meios removem o padrão dado da frente e os %%meios removem o padrão dado da parte de trás. A +( )parte é o padrão e significa "uma ou mais ocorrências de um espaço"
gMale 21/11/15
Engraçado, funcionou no prompt, mas não após a transposição para o arquivo de script bash.
Dr Beco
esquisito. É a mesma versão do bash nos dois casos?
precisa saber é
11
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

OU

# 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")")"
}

OU

# 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]}"
}

OU

# 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")"
}

OU

Com base na expr soulution de moskit ...

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

OU

# 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")")"
}
NOYB
fonte
8

Eu já vi scripts usarem atribuição de variável para fazer o trabalho:

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

O espaço em branco é unido e aparado automaticamente. É preciso ter cuidado com os metacaracteres da concha (risco potencial de injeção).

Eu também recomendaria sempre a substituição de variáveis ​​com aspas duplas nos condicionais do shell:

if [ -n "$var" ]; then

uma vez que algo como a -o ou outro conteúdo na variável pode alterar seus argumentos de teste.

MykennaC
fonte
3
É o uso não citado de $xyzwith echoque faz a coalescência de espaços em branco, não a atribuição de variáveis. Para armazenar o valor aparado na variável em seu exemplo, você precisaria usar xyz=$(echo -n $xyz). Além disso, essa abordagem está sujeita a expansão de nome de caminho potencialmente indesejado (globbing).
usar o seguinte código
isto está errado, o valor na xyzvariável NÃO é aparado.
caesarsol
7
var='   a b c   '
trimmed=$(echo $var)
ultr
fonte
1
Isso não funcionará se houver mais de um espaço entre duas palavras. Tente: echo $(echo "1 2 3")(com dois espaços entre 1, 2 e 3).
precisa saber é
7

Eu simplesmente usaria o sed:

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

a) Exemplo de uso em string de linha única

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

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

Resultado:

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

b) Exemplo de uso em cadeia de linhas múltiplas

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

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

Resultado:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) Nota final:
se você não gosta de usar uma função, para string de linha única, você pode simplesmente usar um comando "mais fácil de lembrar", como:

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

Exemplo:

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

Resultado:

wordA wordB wordC

O uso do descrito acima em seqüências de várias linhas também funcionará , mas observe que ele também cortará qualquer espaço múltiplo interno à direita / à direita, como o GuruM notou nos comentários

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

Resultado:

wordAA
>four spaces before<
>one space before<

Portanto, se você se importa em manter esses espaços, use a função no início da minha resposta!

d) EXPLICAÇÃO da sintaxe sed "localizar e substituir" em seqüências de várias linhas usadas dentro da função trim:

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
}'
Luca Borrione
fonte
Nota: Conforme sugerido por @mkelement, não funcionará para cadeias de linhas múltiplas, embora deva funcionar para cadeias de linhas únicas.
GuruM 18/09/12
1
Você está errado: ele também funciona em seqüências de várias linhas. Basta testá-lo! :)
Luca Borrione 18/09/12
+1 para o uso - facilitou o teste do código. No entanto, o código ainda não funcionará para cadeias de linhas múltiplas. Se você observar atentamente a saída, notará que todos os espaços internos à esquerda / à direita também estão sendo removidos, por exemplo, o espaço na frente da "linha múltipla" é substituído por "linha múltipla". Apenas tente aumentar o número de espaços iniciais / finais em cada linha.
GuruM 18/09/12
Agora entendo o que você quer dizer! Obrigado pela atenção, editei minha resposta.
Luca Borrione 18/09/12
@ "Luca Borrione" - bem-vindo :-) Você explicaria a sintaxe sed que você está usando em trim ()? Também pode ajudar qualquer usuário do seu código a ajustá-lo para outros usos. Também pode até ajudar a encontrar casos extremos para a expressão regular.
GuruM 19/09/12
6

Aqui está uma função trim () que apara e normaliza o espaço em branco

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

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

E outra variante que usa expressões regulares.

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

echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'
Nicholas Sushkin
fonte
A primeira abordagem é complicada, pois não apenas normaliza o espaço em branco interno (substitui todos os espaços internos do espaço em branco por um único espaço cada), mas também está sujeita a globbing (expansão do nome do caminho), de modo que, por exemplo, um *caractere na string de entrada expanda para todos os arquivos e pastas na pasta de trabalho atual. Por fim, se $ IFS estiver definido como um valor não padrão, o corte poderá não funcionar (embora isso seja facilmente solucionado com a adição local IFS=$' \t\n'). O corte é limitado às seguintes formas de espaço em branco: espaços \te \ncaracteres.
usar o seguinte comando
1
A segunda abordagem baseada em expressões regulares é ótima e sem efeitos colaterais, mas em sua forma atual é problemática: (a) no bash v3.2 +, a correspondência por padrão NÃO funcionará, porque a expressão regular deve ser sem aspas para para trabalhar e (b) a própria expressão regular não lida com o caso em que a sequência de entrada é um caractere único e não-espaço cercado por espaços. Para corrigir esses problemas, substituir a iffunção: if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]. Finalmente, a abordagem trata apenas de espaços, não de outras formas de espaço em branco (veja meu próximo comentário).
usar o seguinte comando
2
A função que utiliza expressões regulares lida apenas com espaços e não outras formas de espaços em branco, mas é fácil generalizar: Substitua a iflinha com:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0
6

Use AWK:

echo $var | awk '{gsub(/^ +| +$/,"")}1'
ghostdog74
fonte
Doce que parece funcionar (ex :) $stripped_version=echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1'``
rogerdpack
4
exceto awk não está fazendo nada: echo'ing uma variável sem aspas já retirados espaços em branco
Glenn Jackman
6

As atribuições ignoram os espaços em branco à esquerda e à direita e, como tal, podem ser usados ​​para aparar:

$ var=`echo '   hello'`; echo $var
hello
evanx
fonte
8
Isso não é verdade. É o "eco" que remove os espaços em branco, não a atribuição. No seu exemplo, faça echo "$var"para ver o valor com espaços.
Nicholas Sushkin
2
@NicholasSushkin Um poderia fazer, var=$(echo $var)mas eu não recomendo. Outras soluções apresentadas aqui são preferidas.
xebeche 2/08/2012
5

Isso não tem o problema de globos indesejados, além disso, o espaço em branco interior não é modificado (supondo que $IFS esteja definido como padrão ' \t\n').

Ele lê a primeira linha nova (e não a inclui) ou o final da string, o que ocorrer primeiro, e retira qualquer mistura de espaço e \tcaracteres iniciais e finais . Se você deseja preservar várias linhas (e também retirar novas linhas iniciais e finais), use read -r -d '' var << eof; note, no entanto, que se a sua entrada contiver \neof, ela será cortada imediatamente antes. (Outras formas de espaço em branco, ou seja \r, \fe \v, se não despojado, mesmo se você adicioná-los a US $ IFS).

read -r var << eof
$var
eof
Gregor
fonte
5

Isso removerá todos os espaços em branco da sua String,

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

/substitui a primeira ocorrência e //todas as ocorrências de espaços em branco na sequência. Ou seja, todos os espaços em branco são substituídos por - nada

Alpesh Gediya
fonte
4

Este é o método mais simples que eu já vi. Ele usa apenas o Bash, são apenas algumas linhas, o regexp é simples e corresponde a todas as formas de espaço em branco:

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

Aqui está um exemplo de script para testá-lo:

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."
Blujay
fonte
1
Certamente preferível, por exemplo (deuses!), Bombardear com Python. Só que eu acho que é mais simples e mais geral para corretamente corda alça que contém apenas spaces.Slightly simplificado expressão seria:^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
Ron Burk
4

Python tem uma função strip() que funciona de maneira idêntica à do PHP trim(), então podemos fazer um pouco de Python embutido para criar um utilitário facilmente compreensível para isso:

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

Isso irá aparar os espaços em branco à esquerda e à direita (incluindo novas linhas).

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi
brownhead
fonte
Enquanto isso funcionar, considere oferecer uma solução que não envolva o lançamento de um intérprete python completo apenas para aparar uma string. É apenas um desperdício.
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]
#[]
Razor5900
fonte
3

Eu descobri que precisava adicionar algum código de uma sdiffsaída bagunçada para limpá-lo:

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'

Isso remove os espaços finais e outros caracteres invisíveis.

user1186515
fonte
3

Removendo espaços em um espaço:

(text) | fmt -su
gardziol
fonte