วิธีตัดหลายช่องว่างให้เป็นหนึ่งเดียวโดยใช้ sed?


69

sedบน AIX ไม่ได้ทำในสิ่งที่ฉันคิดว่าควรจะเป็น ฉันพยายามแทนที่ช่องว่างหลายช่องด้วยช่องว่างเดียวในเอาต์พุตของ 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 ควรค้นหาและแทนที่หลายช่องว่าง (/ [] * /) ด้วยช่องว่างเดียว (/ /) สำหรับทั้งกลุ่ม (/ g) ... แต่มันไม่เพียงทำเช่นนั้น ... เว้นวรรคอักขระแต่ละตัว

ผมทำอะไรผิดหรือเปล่า? ฉันรู้ว่ามันต้องเป็นอะไรที่ง่าย ... AIX 5300-06

แก้ไข:ฉันมีคอมพิวเตอร์เครื่องอื่นที่มีฮาร์ดไดรฟ์มากกว่า 10 ตัว ฉันใช้สิ่งนี้เป็นพารามิเตอร์ของโปรแกรมอื่นเพื่อการตรวจสอบ

ปัญหาที่ฉันพบคือ "awk '{print $ 5}' ไม่ทำงานเพราะฉันใช้ $ 1 ฯลฯ ในระยะที่สองและให้ข้อผิดพลาดกับคำสั่งพิมพ์ฉันกำลังมองหารุ่น grep / sed / cut สิ่งที่ดูเหมือนว่าจะทำงานคือ:

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

[] s เป็น "0 หรือมากกว่า" เมื่อฉันคิดว่าพวกเขาหมายถึง "เพียงคนเดียว" การลบเครื่องหมายวงเล็บออกทำให้ทำงานได้ สามคำตอบที่ดีมากอย่างรวดเร็วทำให้ยากที่จะเลือก "คำตอบ"

คำตอบ:


52

การใช้grepซ้ำซ้อนsedสามารถทำได้เช่นเดียวกัน ปัญหาคือในการใช้งานของการ*จับคู่ที่ยัง 0 ช่องว่างคุณต้องใช้\+แทน:

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

หากคุณsedไม่รองรับ\+เมตาคาร์คุณก็ทำได้

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

ดูเหมือนว่า AIX จะไม่สนับสนุน + แต่ดูเหมือนว่าการลบ [] นั้นได้ทำไปแล้ว
WernerCD

ฉันลองใช้รุ่น sed -n ... เกิดอะไรขึ้นฉันมีคอมพิวเตอร์เครื่องอื่นที่มีไดรฟ์มากกว่า 10 ตัวดังนั้นมันจึงเริ่มทำ 1, 10, 11, ฯลฯ ... ฉันพยายามเพิ่มพื้นที่ / hdisk1 / และให้ฉัน "ฟังก์ชั่นที่ไม่รู้จัก" สิ่งที่ดูเหมือนว่าจะทำงานคือ >> iostat | grep "hdisk1" | sed -e's / * / / g '
WernerCD

67

/[ ]*/ตรงกับศูนย์หรือมากกว่าช่องว่างดังนั้นสตริงว่างระหว่างตัวละครที่ตรงกัน

หากคุณกำลังพยายามจับคู่ "หนึ่งหรือหลายช่องว่าง" ใช้หนึ่งในเหล่านี้:

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

อ่า ... [] ทำให้เป็น "ตัวเลือก" นั่นอธิบายได้
WernerCD

5
@WernerCD ไม่*ทำให้ "เป็นตัวเลือก" [ ]เพียงแค่สร้างรายการของตัวละครที่มีตัวละครเพียงตัวเดียวในนั้น (เว้นวรรค) มันเป็นปริมาณ*ที่หมายถึง "ศูนย์หรือมากกว่าของสิ่งก่อนหน้า"
เกล็นแจ็

อ่า ... ถ้าจะให้แม่นยำกว่านี้ให้เปลี่ยนจากช่องว่างเดียว / * / เป็นช่องว่างสองเท่าตอนนั้น ฉัน gottcha
WernerCD

ฉันพยายามค้นหารูปแบบที่ค้นหาเฉพาะช่องว่างสองเท่าเท่านั้นและมันก็ใช้งานได้ดี
minhas23

6
+1 สำหรับtr -s ' 'โซลูชันที่ง่ายที่สุด
Andrejs

12

คุณสามารถเปลี่ยนผู้ประกอบการไปยัง* +คุณกำลังจับคู่อักขระก่อนหน้าเป็นศูนย์หรือมากกว่าซึ่งตรงกับอักขระทุกตัวเพราะทุกอย่างที่ไม่ใช่ช่องว่างคือ ... um ... ศูนย์อินสแตนซ์ของพื้นที่ คุณต้องจับคู่หนึ่งหรือมากกว่านั้น จริง ๆ แล้วมันจะดีกว่าที่จะจับคู่สองหรือมากกว่า

คลาสอักขระที่อยู่ในวงเล็บนั้นไม่จำเป็นสำหรับการจับคู่หนึ่งอักขระ คุณสามารถใช้:

s/  \+/ /g

... เว้นเสียแต่ว่าคุณต้องการจับคู่แท็บหรือช่องว่างประเภทอื่นด้วยคลาสตัวละครเป็นแนวคิดที่ดี


ดูเหมือนว่า AIX จะไม่สนับสนุน +
WernerCD

1
@WernerCD: จากนั้นลองs/ */ /g(ที่มีช่องว่างสามช่องการจัดรูปแบบความคิดเห็นจะยุบลง) ตัวดำเนินการดาวจะทำให้อักขระก่อนหน้าเป็นตัวเลือกดังนั้นหากคุณต้องจับคู่สองตัวหรือมากกว่ากับตัวคุณคุณจะต้องจับคู่สองตัวแรก (สองช่องว่าง) จากนั้นเพิ่มช่องว่างที่สามและดาวเพื่อทำให้ช่องว่างที่สาม
Caleb

3
@userunknown: ที่จริงฉันไม่ได้ผสมสองสิ่งเลยทุกคนคือ :) การแทนที่ช่องว่างเดียวด้วยช่องว่างเดียวนั้นไม่มีจุดหมายคุณจะต้องดำเนินการนี้กับการจับคู่ที่มีช่องว่างตามลำดับอย่างน้อยสองช่อง ช่องว่างสองช่องและช่องว่างบวกหรือสามช่องและดาวเป็นสิ่งที่ต้องการอย่างแท้จริง
Caleb

@userunknown: มันไม่ได้เป็นเรื่องใหญ่ที่มันแค่เสียเวลาในการประมวลผลไปนิดหน่อย
คาเลบ

8

คุณสามารถจับคู่เหตุการณ์ที่เกิดขึ้นล่าสุดในลำดับของสิ่งใดก็ได้เช่น:

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

และเพื่อให้คุณอยู่ในเส้นทางที่ถูกต้อง แต่แทนที่จะแทนที่ลำดับด้วยการเว้นวรรค - แทนที่ด้วยการเกิดครั้งสุดท้าย - พื้นที่เดียว วิธีที่ว่าถ้าลำดับของช่องว่างจะถูกจับคู่ตามลำดับจะลดลงไปพื้นที่เดียว แต่ถ้าสตริงจะถูกจับคู่แล้วสตริงจะถูกแทนที่ด้วยตัวเอง - และไม่มีอันตรายใด ๆ เหม็นไม่มี ตัวอย่างเช่น:

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

เอาท์พุท

# 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

จากข้อมูลทั้งหมดที่กล่าวมาน่าจะดีกว่าหากหลีกเลี่ยง regexps อย่างสมบูรณ์ในสถานการณ์นี้และควรทำแทน:

tr -s \  <infile

4
+1 เพื่อความเรียบง่ายของคำตอบจริงiostat | tr -s \
Wildcard

'tr -s \' เหมือนกับ 'tr -s ""' ทำให้ฉันรู้ว่าสามารถส่งผ่านช่องว่างเป็นอาร์กิวเมนต์ในสตริงได้โดยใช้ "\" ฉันเห็นว่ามันสามารถใช้ในเชลล์สคริปต์ได้เช่นกัน แอพพลิเคชั่นสุดยอด
randominstanceOfLivingThing

5

โปรดสังเกตว่าคุณสามารถทำสิ่งที่คุณพยายามได้เช่นกัน

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

โดย

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

ซึ่งอาจมีประโยชน์อย่างยิ่งหากคุณพยายามเข้าถึงฟิลด์อื่นในภายหลังและ / หรือคำนวณบางสิ่งเช่นนี้:

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

ดีมาก. เวอร์ชันแรกใช้งานได้ กล่อง AIX ของฉันดูเหมือนจะไม่ชอบกล่องที่สอง กล่องทั้งสามออก: "$ [ใหม่ / 1024] Mb" เครื่องมือตรวจสอบที่ฉันใช้มีการแปลงสำหรับรายงานดังนั้นจึงไม่ใช่สิ่งที่ "จำเป็น" สำหรับฉัน แต่ฉันชอบ
WernerCD

@enzotib whileขอบคุณสำหรับการแก้ไข
rozcietrzewiacz

@WernerCD อ้านี่$[ .. ]ใช้ได้ใน bash รุ่นล่าสุด (อาจจะ zsh ด้วย) ฉันปรับปรุงคำตอบเป็นแบบพกพามากขึ้น$(( .. ))แทน
rozcietrzewiacz

นั่นเป็นการหลอกลวง ฉันจะต้องมองหามัน โก๋.
WernerCD

0

คุณสามารถใช้สคริปต์ต่อไปนี้เพื่อแปลงช่องว่างหลายช่องให้เป็นช่องว่างเดียว TAB หรือสตริงอื่น ๆ :

$ 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"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.