Come spogliare più spazi su uno usando sed?


69

sedsu AIX non sta facendo quello che penso dovrebbe. Sto cercando di sostituire più spazi con un singolo spazio nell'output di IOSTAT:

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

#iostat|grep "hdisk1"|sed -e"s/[ ]*/ /g"
 h d i s k 1 0 . 1 6 . 3 0 . 5 6 3 3 4 5 8 8 0 1 1 2 4 3 2 3 5 4

sed dovrebbe cercare e sostituire (s) più spazi (/ [] * /) con un singolo spazio (/ /) per l'intero gruppo (/ g) ... ma non lo sta solo facendo ... spaziando ogni personaggio.

Che cosa sto facendo di sbagliato? So che deve essere qualcosa di semplice ... AIX 5300-06

modifica: ho un altro computer con oltre 10 dischi rigidi. Sto usando questo come parametro per un altro programma a scopo di monitoraggio.

Il problema che ho riscontrato è che "awk '{print $ 5}' non ha funzionato perché sto usando $ 1, ecc. Nella fase secondaria e ha dato errori con il comando Print. Stavo cercando una versione grep / sed / cut Ciò che sembra funzionare è:

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

Gli [] erano "0 o più" quando pensavo che significassero "solo uno". La rimozione delle parentesi ha funzionato. Tre risposte molto valide rendono molto rapidamente difficile scegliere la "risposta".

Risposte:


52

L'uso di grepè ridondante, sedpuò fare lo stesso. Il problema è nell'uso di *quella corrispondenza anche 0 spazi, devi \+invece usare :

iostat | sed -n '/hdisk1/s/ \+/ /gp'

Se il tuo sednon supporta \+metachar, allora fallo

iostat | sed -n '/hdisk1/s/  */ /gp'

AIX non sembra supportare +, ma la rimozione di [] sembra aver fatto il trucco.
WernerCD,

Ho provato ad usare la versione sed -n ... quello che succede è che ho un altro computer con oltre 10 unità, quindi inizia a fare 1, 10, 11, ecc ... Ho provato ad aggiungere uno spazio / hdisk1 / e mi ha dato una "funzione non riconosciuta". ciò che sembra funzionare è >> iostat | grep "hdisk1" | sed -e's / * / / g '
WernerCD

67

/[ ]*/corrisponde a zero o più spazi, quindi la stringa vuota tra i caratteri corrisponde.

Se stai cercando di abbinare "uno o più spazi", usa uno di questi:

... | sed 's/  */ /g'
... | sed 's/ \{1,\}/ /g'
... | tr -s ' '

Ahh ... [] lo rende "opzionale". Questo lo spiega.
WernerCD,

5
@WernerCD, no lo *rende "opzionale". [ ]crea solo un elenco di personaggi con solo un personaggio (uno spazio). È il quantificatore *che significa "zero o più della cosa precedente"
Glenn Jackman,

Ahh ... quindi per essere più precisi, cambiarlo da un singolo spazio / * /, a un doppio spazio è ciò che ha fatto allora. Io gottcha.
WernerCD,

Stavo cercando di cercare uno schema che cerchi solo doppi spazi e ha funzionato bene
minhas23

6
+1 per la tr -s ' 'soluzione più semplice
Andrejs

12

Cambia il tuo *operatore in a +. Stai abbinando zero o più del carattere precedente, che corrisponde a ogni personaggio perché tutto ciò che non è uno spazio è ... um ... zero istanze di spazio. Devi abbinare UNO o più. In realtà sarebbe meglio abbinarne due o più

Anche la classe di caratteri tra parentesi non è necessaria per abbinare un carattere. Puoi semplicemente usare:

s/  \+/ /g

... a meno che tu non voglia abbinare schede o altri tipi di spazi, allora la classe di caratteri è una buona idea.


AIX non sembra supportare +.
WernerCD,

1
@WernerCD: Quindi prova s/ */ /g(ovvero con tre spazi, la formattazione dei commenti li sta comprimendo). L'operatore stella renderà facoltativo il carattere precedente, quindi se devi abbinarne due o più con esso devi abbinare tu stesso i primi due (due spazi), quindi aggiungi un terzo spazio e una stella per rendere facoltativi il terzo e i seguenti spazi.
Caleb,

3
@userunknown: In realtà non sto mescolando due cose, tutti gli altri lo sono :) Sostituire un singolo spazio con un singolo spazio è inutile, devi solo fare questa azione su partite che hanno almeno due spazi sequenziali. Due spazi vuoti e un segno più o tre spazi vuoti e una stella sono esattamente ciò che è necessario.
Caleb,

@userunknown: non è un grosso problema, è solo uno spreco di tempo di elaborazione e butta via cose come i contatori delle partite.
Caleb,

8

Puoi sempre abbinare l'ultima occorrenza in una sequenza di qualcosa come:

s/\(sequence\)*/\1/

E così sei sulla strada giusta, ma invece di sostituire la sequenza con uno spazio - sostituiscilo con la sua ultima occorrenza - un singolo spazio. In questo modo se una sequenza di spazi viene abbinata, la sequenza viene ridotta a un singolo spazio, ma se la stringa nulla viene abbinata, la stringa nulla viene sostituita con se stessa e nessun danno, nessun fallo. Quindi, per esempio:

sed 's/\( \)*/\1/g' <<\IN                                    
# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

IN

PRODUZIONE

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty: tin tout avg-cpu: % user % sys % idle % iowait
 0.2 31.8 9.7 4.9 82.9 2.5

Disks: % tm_act Kbps tps Kb_read Kb_wrtn
hdisk9 0.2 54.2 1.1 1073456960 436765896
hdisk7 0.2 54.1 1.1 1070600212 435678280
hdisk8 0.0 0.0 0.0 0 0
hdisk6 0.0 0.0 0.0 0 0
hdisk1 0.1 6.3 0.5 63344916 112429672
hdisk0 0.1 5.0 0.2 40967838 98574444
cd0 0.0 0.0 0.0 0 0
hdiskpower1 0.2 108.3 2.3 2144057172 872444176

# iostat | grep hdisk1
hdisk1 0.1 6.3 0.5 63345700 112431123

Detto questo, è probabilmente molto meglio evitare completamente le regexps in questa situazione e invece:

tr -s \  <infile

4
+1 per la semplicità della vera risposta,iostat | tr -s \
Wildcard il

'tr -s \' è uguale a 'tr -s ""'. Mi ha fatto capire che lo spazio può essere passato come argomento nella stringa scappando con "\". Vedo che può essere utilizzato anche negli script di shell. Bella applicazione.
randominstanceOfLivingThing

5

Nota che puoi anche fare ciò che cerchi, cioè

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

di

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$re"; done

che potrebbe essere particolarmente utile se in seguito si tenta di accedere anche ad altri campi e / o calcolare qualcosa, come questo:

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$(( re/1024 )) Mb"; done

Molto bella. La prima versione funziona. Le mie scatole AIX non sembrano gradire la seconda. Output di tutte e tre le caselle: "$ [re / 1024] Mb". Lo strumento di monitoraggio che sto usando ha conversioni per i rapporti, quindi non è una cosa "necessaria" per me, ma mi piace.
WernerCD,

@enzotib Grazie per aver corretto il while.
rozcietrzewiacz,

@WernerCD Ah, questo $[ .. ]è probabilmente disponibile nelle recenti versioni di bash (forse anche zsh). Ho aggiornato la risposta a un più portatile $(( .. ))invece.
rozcietrzewiacz,

Questo ha funzionato. Dovrò cercarlo. Snazzy.
WernerCD,

0

È possibile utilizzare il seguente script per convertire più spazi in un singolo spazio, una TAB o qualsiasi altra stringa:

$ ls | compress_spaces.sh       # converts multiple spaces to one
$ ls | compress_spaces.sh TAB   # converts multiple spaces to a single tab character
$ ls | compress_spaces.sh TEST  # converts multiple spaces to the phrase TEST
$ compress_spaces.sh help       # show the help for this command

compress_spaces.sh

function show_help()
{
  IT=$(CAT <<EOF

  usage: {REPLACE_WITH}

  NOTE: If you pass in TAB, then multiple spaces are replaced with a TAB character

  no args -> multiple spaces replaced with a single space
  TAB     -> multiple spaces replaced with a single tab character
  TEST    -> multiple spaces replaced with the phrase "TEST"

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show help if we're not getting data from stdin
if [ -t 0 ]; then
  show_help
fi

REPLACE_WITH=${1:-' '}

if [ "$REPLACE_WITH" == "tab" ]
then
  REPLACE_WITH=$'\t'
fi
if [ "$REPLACE_WITH" == "TAB" ]
then
  REPLACE_WITH=$'\t'
fi

sed "s/ \{1,\}/$REPLACE_WITH/gp"
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.