Como remover espaços em branco à direita com sed?

113

Eu tenho um script de shell simples que remove o espaço em branco à direita de um arquivo. Existe alguma maneira de tornar este script mais compacto (sem criar um arquivo temporário)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp
Viktor
fonte
2
Você pode usar em mvvez de cate rm. Por que você está usando catassim? Por que não usar cp?
Pausado até novo aviso.
1
Usei o conhecimento que aprendi com essa pergunta para criar um script de shell para remover recursivamente os espaços em branco à direita .
David Tuite
1
Na verdade, sua solução é melhor ao usar o MinGW devido a um bug no sed no Windows: stackoverflow.com/questions/14313318/…
Cody Piersall
Observe que usar catpara sobrescrever o arquivo original em vez de mvrealmente substituir os dados do arquivo original (ou seja, não quebrará os links físicos). Usar sed -icomo proposto em muitas soluções não fará isso. IOW, continue fazendo o que está fazendo.
William Pursell de

Respostas:

157

Você pode usar a opção local -ide sedpara Linux e Unix:

sed -i 's/[ \t]*$//' "$1"

Esteja ciente de que a expressão excluirá trailing t's no OSX (você pode usar gsedpara evitar esse problema). Ele também pode excluí-los do BSD.

Se você não tiver gsed, aqui está a sintaxe sed correta (mas difícil de ler) no OSX:

sed -i '' -E 's/[ '$'\t'']+$//' "$1"

Três strings entre aspas simples se tornam concatenadas em um único argumento / expressão. Não há operador de concatenação no bash, você apenas coloca as strings uma após a outra, sem espaço entre elas.

O é $'\t'resolvido como um caractere de tabulação literal em bash (usando aspas ANSI-C ), portanto, a tabulação é concatenada corretamente na expressão.

codadicto
fonte
1
Recebo o seguinte em minha máquina que não consigo atualizar: sed: Not a recognized flag: i
javaPlease42
2
hm. também tem erros no sentido de que removerá todos os "t" s restantes :)
Boa pessoa
2
"sed: Sinalizador não reconhecido: i -" Isso acontece no OSX. Você precisa adicionar uma extensão para o arquivo de backup após -i em Macs. por exemplo: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo
1
@GoodPerson Se você não estava brincando, provavelmente esqueceu de escapar do t:) \té uma guia, para quem ainda não sabe.
Sean Allred
2
@SeanAllred não estava brincando: está totalmente quebrado, a menos que você esteja usando GNU sed (que está quebrado de muitas outras maneiras)
Boa pessoa
59

Pelo menos em Mountain Lion, a resposta de Viktor também removerá o caractere 't' quando estiver no final de uma linha. O seguinte corrige esse problema:

sed -i '' -e's/[[:space:]]*$//' "$1"
Acrollet
fonte
1
Meu sed também queria uma -Eindicação de "expressões regulares estendidas (modernas)"
Jared Beck
Funciona perfeitamente no OS X. Muito obrigado.
jww
1
A resposta do codaddict tem o mesmo problema no OS X (agora macOS). Esta é a única solução nesta plataforma.
Franklin Yu
@JaredBeck Mine sedem El Capitan não.
Franklin Yu
19

Obrigado ao codaddict por sugerir a -iopção.

O seguinte comando resolve o problema no Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"
Viktor
fonte
7
Como @acrollet diz, você não pode usar \tcom o sed diferente do GNU sed e ele é interpretado como uma letra literal t. O comando apenas parece funcionar, provavelmente porque não há TABs no espaço em branco à direita nem a tno final de uma frase em seu arquivo. Usar ''sem especificar um sufixo de backup não é recomendado.
Scrutinizer
13

É melhor também citar $ 1:

sed -i.bak 's/[[:blank:]]*$//' "$1"
Escrutinador
fonte
5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2
Sandip Patel - SM
fonte
1
Ei, era isso que eu precisava! As outras soluções sed postadas tiveram problemas de integração com uma atribuição de variável piped (e piped e piped ...) no meu script bash, mas o seu funcionou imediatamente.
Eric L.
4

Eu tenho um script em meu .bashrc que funciona em OSX e Linux (apenas bash!)

function trim_trailing_space() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will trim (in place) trailing spaces in the given file (remove unwanted spaces at end of lines)"
    echo "Usage :"
    echo "$FUNCNAME file"
    return
  fi
  local file=$1
  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    sed -E -i ''  's/[[:space:]]*$//' $file
  else
    sed -i  's/[[:space:]]*$//' $file
  fi
}

ao qual acrescento:

SRC_FILES_EXTENSIONS="js|ts|cpp|c|h|hpp|php|py|sh|cs|sql|json|ini|xml|conf"

function find_source_files() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will list sources files (having extensions $SRC_FILES_EXTENSIONS)"
    echo "Usage :"
    echo "$FUNCNAME folder"
    return
  fi
  local folder=$1

  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    find -E $folder -iregex '.*\.('$SRC_FILES_EXTENSIONS')'
  else
    #Rhahhh, lovely
    local extensions_escaped=$(echo $SRC_FILES_EXTENSIONS | sed s/\|/\\\\\|/g)
    #echo "extensions_escaped:$extensions_escaped"
    find $folder -iregex '.*\.\('$extensions_escaped'\)$'
  fi
}

function trim_trailing_space_all_source_files() {
  for f in $(find_source_files .); do trim_trailing_space $f;done
}
Pascal T.
fonte
3

Para quem busca eficiência (muitos arquivos para processar ou arquivos enormes), usar o +operador de repetição em vez de *torna o comando mais do que duas vezes mais rápido.

Com GNU sed:

sed -Ei 's/[ \t]+$//' "$1"
sed -i 's/[ \t]\+$//' "$1"   # The same without extended regex

Eu também fiz um benchmarking rapidamente de outra coisa: usar em [ \t]vez de [[:space:]]também acelera significativamente o processo (GNU sed v4.4):

sed -Ei 's/[ \t]+$//' "$1"

real    0m0,335s
user    0m0,133s
sys 0m0,193s

sed -Ei 's/[[:space:]]+$//' "$1"

real    0m0,838s
user    0m0,630s
sys 0m0,207s

sed -Ei 's/[ \t]*$//' "$1"

real    0m0,882s
user    0m0,657s
sys 0m0,227s

sed -Ei 's/[[:space:]]*$//' "$1"

real    0m1,711s
user    0m1,423s
sys 0m0,283s
yolenoyer
fonte
1

Apenas por diversão:

#!/bin/bash

FILE=$1

if [[ -z $FILE ]]; then
   echo "You must pass a filename -- exiting" >&2
   exit 1
fi

if [[ ! -f $FILE ]]; then
   echo "There is not file '$FILE' here -- exiting" >&2
   exit 1
fi

BEFORE=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

# >>>>>>>>>>
sed -i.bak -e's/[ \t]*$//' "$FILE"
# <<<<<<<<<<

AFTER=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

if [[ $? != 0 ]]; then
   echo "Some error occurred" >&2
else
   echo "Filtered '$FILE' from $BEFORE characters to $AFTER characters"
fi
David Tonhofer
fonte
0

No caso específico de sed, a -iopção que outros já mencionaram é de longe a mais simples e sã.

No caso mais geral sponge, a partir da moreutilscoleção, faz exatamente o que você deseja: permite substituir um arquivo pelo resultado do processamento, de uma forma especificamente projetada para evitar que a etapa de processamento tropece em si mesma, sobrescrevendo o próprio arquivo que está trabalhando em. Para citar a spongepágina de manual:

sponge lê a entrada padrão e a grava no arquivo especificado. Ao contrário de um redirecionamento de shell, a esponja absorve toda a sua entrada antes de gravar o arquivo de saída. Isso permite a construção de pipelines que lêem e gravam no mesmo arquivo.

https://joeyh.name/code/moreutils/

Dan Martinez
fonte
-1

Para retirar apenas os espaços em branco (no meu caso, espaços e tabulações) das linhas com pelo menos um caractere diferente de espaço em branco (desta forma, as linhas recuadas vazias não são tocadas):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
phk
fonte