Come rimuovere gli spazi vuoti finali con sed?


113

Ho un semplice script di shell che rimuove gli spazi vuoti finali da un file. C'è un modo per rendere questo script più compatto (senza creare un file temporaneo)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp

2
Puoi usare al mvposto di cate rm. Perché usi catcosì comunque? Perché non usare cp?
In pausa fino a nuovo avviso.

1
Ho usato la conoscenza che ho imparato da questa domanda per creare uno script di shell per la rimozione ricorsiva degli spazi vuoti finali .
David Tuite

1
La tua soluzione è effettivamente migliore quando usi MinGW a causa di un bug in sed su Windows: stackoverflow.com/questions/14313318/…
Cody Piersall


Notare che l'uso catdi sovrascrivere il file originale anziché mvsostituirà effettivamente i dati nel file originale (cioè, non interromperà i collegamenti fisici). Utilizzare sed -icome proposto in molte soluzioni non lo farà. IOW, continua a fare quello che stai facendo.
William Pursell

Risposte:


157

Puoi utilizzare l'opzione sul posto -idi sedper Linux e Unix:

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

Tieni presente che l'espressione cancellerà i finali tsu OSX (puoi usarli gsedper evitare questo problema). Potrebbe eliminarli anche su BSD.

Se non hai gsed, ecco la sintassi corretta (ma difficile da leggere) di sed su OSX:

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

Tre stringhe con virgolette singole alla fine vengono concatenate in un singolo argomento / espressione. Non esiste un operatore di concatenazione in bash, basta posizionare le stringhe una dopo l'altra senza spazi intermedi.

Il si $'\t'risolve come un carattere di tabulazione letterale in bash (usando le virgolette ANSI-C ), quindi la tabulazione è correttamente concatenata nell'espressione.


1
Sulla mia macchina ottengo quanto segue che non riesco ad aggiornare: sed: Not a recognized flag: i
javaPlease42

2
hm. è anche difettoso, nel senso che rimuoverà tutte le "t" finali :)
Good Person

2
"sed: flag non riconosciuto: i -" Questo accade su OSX. È necessario aggiungere un'estensione per il file di backup dopo -i su Mac. ad esempio: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo

1
@GoodPerson Se non stavi scherzando, probabilmente dimenticherai di sfuggire alla t:) \tè una scheda, per coloro che potrebbero non saperlo già.
Sean Allred

2
@SeanAllred non stava scherzando: è completamente rotto a meno che tu non stia usando GNU sed (che è rotto in tanti altri modi)
Good Person

59

Almeno su Mountain Lion, la risposta di Viktor rimuoverà anche il carattere "t" quando si trova alla fine di una riga. Le seguenti soluzioni risolvono il problema:

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

1
Il mio sed voleva anche -Eun'indicazione "espressioni regolari estese (moderne)"
Jared Beck

Funziona alla grande su OS X. Grazie mille.
jww

1
La risposta di codaddict ha lo stesso problema su OS X (ora macOS). Questa è l'unica soluzione su questa piattaforma.
Franklin Yu

@JaredBeck Il mio sedsu El Capitan no.
Franklin Yu

19

Grazie a codaddict per aver suggerito l' -iopzione.

Il seguente comando risolve il problema su Snow Leopard

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


7
Come dice @acrollet, non puoi usare \tcon sed diversi da GNU sed e viene interpretato come una lettera letterale t. Il comando sembra funzionare solo, probabilmente perché non ci sono TAB nello spazio vuoto finale né talla fine di una frase nel tuo file. L'uso ''senza specificare un suffisso di backup non è consigliato.
Scrutinizer

13

È meglio citare anche $ 1:

sed -i.bak 's/[[:blank:]]*$//' "$1"

5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2

1
Ehi, è proprio quello di cui avevo bisogno! Le altre soluzioni sed pubblicate hanno riscontrato problemi di integrazione con un'assegnazione di variabile in pipe (e in pipe e in pipe ...) nel mio script bash, ma il tuo ha funzionato immediatamente.
Eric L.

4

Ho uno script nel mio .bashrc che funziona su OSX e Linux (solo 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
}

a cui aggiungo:

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
}

3

Per coloro che cercano efficienza (molti file da elaborare o file enormi), l'utilizzo +dell'operatore di ripetizione invece di *rende il comando più del doppio più veloce.

Con GNU sed:

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

Ho anche valutato rapidamente qualcos'altro: l'utilizzo [ \t]invece di [[:space:]]accelera notevolmente il 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

1

Solo per divertimento:

#!/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

0

Nel caso specifico di sed, l' -iopzione che altri hanno già menzionato è di gran lunga la più semplice e sana.

Nel caso più generale sponge, dalla moreutilscollezione, fa esattamente quello che vuoi: ti permette di sostituire un file con il risultato di elaborarlo, in un modo appositamente studiato per evitare che la fase di elaborazione inciampi su se stessa sovrascrivendo proprio il file lavorandoci. Per citare la spongepagina man:

sponge legge l'input standard e lo scrive nel file specificato. A differenza di un reindirizzamento della shell, sponge assorbe tutto il suo input prima di scrivere il file di output. Ciò consente di costruire pipeline che leggono e scrivono nello stesso file.

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


-1

Per rimuovere solo gli spazi bianchi (nel mio caso spazi e tabulazioni) dalle righe con almeno un carattere diverso da spazi (in questo modo le righe vuote rientrate non vengono toccate):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.