Bagaimana cara menghapus beberapa spasi menjadi satu menggunakan sed?

69

sedpada AIX tidak melakukan apa yang saya pikir seharusnya. Saya mencoba mengganti beberapa spasi dengan satu spasi dalam output 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 harus mencari & mengganti beberapa spasi (/ [] * /) dengan satu spasi (/ /) untuk seluruh grup (/ g) ... tetapi tidak hanya melakukan itu ... mengatur jarak setiap karakter.

Apa yang saya lakukan salah? Saya tahu ini pasti sesuatu yang sederhana ... AIX 5300-06

sunting: Saya memiliki komputer lain yang memiliki 10+ hard drive. Saya menggunakan ini sebagai parameter ke program lain untuk tujuan pemantauan.

Masalah yang saya temui adalah "awk '{print $ 5}' tidak berfungsi karena saya menggunakan $ 1, dll pada tahap sekunder dan memberikan kesalahan dengan perintah Print. Saya mencari versi grep / sed / cut Apa yang tampaknya berhasil adalah:

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

[] Adalah "0 atau lebih" ketika saya pikir artinya "hanya satu". Menghapus braket membuatnya berfungsi. Tiga jawaban yang sangat bagus sangat cepat membuat sulit untuk memilih "jawaban".

WernerCD
sumber

Jawaban:

52

Penggunaannya grepberlebihan, sedbisa melakukan hal yang sama. Masalahnya adalah dalam penggunaan *pertandingan itu juga 0 spasi, Anda harus menggunakan \+sebagai gantinya:

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

Jika Anda sedtidak mendukung \+metachar, maka lakukan

iostat | sed -n '/hdisk1/s/  */ /gp'
enzotib
sumber
AIX tampaknya tidak mendukung +, tetapi penghapusan [] tampaknya telah berhasil.
WernerCD
Saya mencoba menggunakan versi sed -n ... yang terjadi adalah saya memiliki komputer lain yang memiliki 10+ drive sehingga mulai melakukan 1, 10, 11, dll ... Saya mencoba menambahkan spasi / hdisk1 / dan itu memberi saya "fungsi tidak dikenal". apa yang tampaknya berfungsi adalah >> iostat | grep "hdisk1" | sed -e's / * / / g '
WernerCD
67

/[ ]*/cocok dengan nol atau lebih banyak ruang, sehingga string kosong antara karakter cocok.

Jika Anda mencoba mencocokkan "satu atau lebih spasi", gunakan salah satu dari ini:

... | sed 's/  */ /g'
... | sed 's/ \{1,\}/ /g'
... | tr -s ' '
glenn jackman
sumber
Ahh ... [] menjadikannya "opsional". Itu menjelaskannya.
WernerCD
5
@WernerCD, tidak *membuatnya "opsional". [ ]hanya membuat daftar karakter dengan hanya satu karakter di dalamnya (spasi). Ini adalah penjumlahan *yang berarti "nol atau lebih dari yang sebelumnya"
glenn jackman
Ahh ... jadi lebih akurat, mengubahnya dari satu ruang / * /, ke ruang ganda adalah apa yang melakukannya. Saya harus.
WernerCD
Saya mencoba mencari pola yang hanya mencari spasi ganda dan itu berhasil
minhas23
6
+1 untuk tr -s ' 'solusi paling sederhana
Andrejs
12

Ubah *operator Anda menjadi a +. Anda mencocokkan nol atau lebih dari karakter sebelumnya, yang cocok dengan setiap karakter karena semua yang bukan spasi adalah ... um ... nol contoh spasi. Anda harus mencocokkan SATU atau lebih. Sebenarnya akan lebih baik untuk mencocokkan dua atau lebih

Kelas karakter yang dikurung juga tidak perlu untuk mencocokkan satu karakter. Anda bisa menggunakan:

s/  \+/ /g

... kecuali jika Anda ingin mencocokkan tab atau jenis ruang lainnya juga, maka kelas karakter adalah ide yang bagus.

Caleb
sumber
AIX tampaknya tidak mendukung +.
WernerCD
1
@WernerCD: Lalu coba s/ */ /g(itu dengan tiga spasi, format komentar runtuh). Operator bintang akan membuat karakter sebelumnya opsional, jadi jika Anda mencocokkan dua atau lebih dengan itu Anda harus mencocokkan dua sendiri pertama (dua spasi) kemudian tambahkan spasi ketiga dan bintang untuk membuat spasi ketiga dan selanjutnya opsional.
Caleb
3
@userunknown: Sebenarnya saya tidak mencampur dua hal sama sekali, semua orang adalah :) Mengganti satu ruang dengan satu ruang tidak ada gunanya, Anda hanya perlu melakukan tindakan ini pada pertandingan yang memiliki setidaknya dua ruang berurutan. Dua kosong dan satu plus atau tiga kosong dan satu bintang adalah persis apa yang dibutuhkan.
Caleb
@userunknown: Ini bukan masalah besar, hanya buang-buang waktu pemrosesan dan membuang hal-hal seperti penghitung pertandingan.
Caleb
8

Anda selalu dapat mencocokkan kejadian terakhir dalam urutan yang seperti:

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

Jadi Anda berada di jalur yang benar, tetapi alih-alih mengganti urutan dengan spasi - ganti dengan kejadian terakhir - satu ruang. Dengan cara itu jika urutan ruang yang cocok maka urutan dikurangi menjadi satu ruang, tetapi jika string nol cocok maka null string diganti dengan dirinya sendiri - dan tidak membahayakan, tidak busuk. Jadi, misalnya:

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

KELUARAN

# 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

Semua yang dikatakan, mungkin jauh lebih baik untuk menghindari regexps sepenuhnya dalam situasi ini dan lakukan sebagai gantinya:

tr -s \  <infile
mikeserv
sumber
4
+1 untuk kesederhanaan jawaban yang sebenarnya,iostat | tr -s \
Wildcard
'tr -s \' sama dengan 'tr -s ""'. Membuat saya sadar bahwa ruang dapat dilewatkan sebagai argumen dalam string dengan melarikan diri dengan "\". Saya melihat bahwa itu dapat digunakan dalam skrip shell juga. Aplikasi keren.
randominstanceOfLivingThing
5

Perhatikan bahwa Anda juga dapat melakukan apa yang Anda usahakan, yaitu

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

oleh

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

yang mungkin sangat berguna jika nanti Anda mencoba mengakses bidang lain juga dan / atau menghitung sesuatu - seperti ini:

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$(( re/1024 )) Mb"; done
rozcietrzewiacz
sumber
Sangat bagus. Versi pertama berfungsi. Kotak AIX saya sepertinya tidak suka yang kedua. Ketiga kotak keluaran: "$ [re / 1024] Mb". Alat pemantauan yang saya gunakan memiliki konversi untuk laporan sehingga itu bukan hal yang "dibutuhkan" bagi saya, tetapi saya menyukainya.
WernerCD
@enzotib Terima kasih telah memperbaiki while.
rozcietrzewiacz
@WernerCD Ah, ini $[ .. ]mungkin tersedia di versi bash terbaru (mungkin zsh juga). Saya memperbarui jawaban ke yang lebih portabel $(( .. )).
rozcietrzewiacz
Itu berhasil. Saya harus mencarinya. Manis.
WernerCD
0

Anda dapat menggunakan skrip berikut untuk mengonversi beberapa spasi menjadi satu spasi, TAB atau string lain:

$ 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"
Taman Brad
sumber