จะลบช่องว่างต่อท้ายด้วย sed ได้อย่างไร?


113

ฉันมีเชลล์สคริปต์ง่ายๆที่ลบช่องว่างต่อท้ายออกจากไฟล์ มีวิธีใดบ้างที่จะทำให้สคริปต์นี้กะทัดรัดมากขึ้น (โดยไม่ต้องสร้างไฟล์ชั่วคราว)

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

2
คุณสามารถใช้mvแทนและcat rmทำไมคุณถึงใช้catแบบนั้นล่ะ? ทำไมไม่ใช้cp?
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

1
ผมใช้ความรู้ที่ผมได้เรียนรู้จากคำถามนี้เพื่อสร้างเชลล์สคริปต์สำหรับซ้ำลบช่องว่างต่อท้าย
David Tuite

1
โซลูชันของคุณดีขึ้นจริงเมื่อใช้ MinGW เนื่องจากข้อผิดพลาดใน Sed บน Windows: stackoverflow.com/questions/14313318/…
Cody Piersall


โปรดทราบว่าการใช้catเพื่อเขียนทับไฟล์ต้นฉบับแทนที่จะmvแทนที่ข้อมูลในไฟล์ต้นฉบับจริงๆ (กล่าวคือจะไม่ทำลายฮาร์ดลิงก์) การใช้sed -iตามที่เสนอในหลาย ๆ โซลูชันจะไม่ทำเช่นนั้น IOW เพียงแค่ทำในสิ่งที่คุณกำลังทำอยู่
William Pursell

คำตอบ:


157

คุณสามารถใช้ตัวเลือกในสถานที่-iของsedสำหรับ Linux และ Unix:

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

โปรดทราบว่านิพจน์จะลบการต่อท้ายtบน OSX (คุณสามารถใช้gsedเพื่อหลีกเลี่ยงปัญหานี้ได้) มันอาจลบพวกเขาใน BSD ด้วย

หากคุณไม่มี gsed นี่คือไวยากรณ์ sed ที่ถูกต้อง (แต่อ่านยาก) บน OSX:

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

ในท้ายที่สุดสตริงที่ยกมาสามรายการจะเชื่อมต่อกันเป็นอาร์กิวเมนต์ / นิพจน์เดียว ไม่มีตัวดำเนินการเชื่อมต่อใน bash คุณเพียงแค่วางสตริงทีละบรรทัดโดยไม่มีช่องว่างระหว่าง

$'\t'แก้ไขเป็นตัวอักษรแท็บตัวละครในทุบตี (โดยใช้มาตรฐาน ANSI-C quoting ) ดังนั้นแท็บจะถูกตัดแบ่งได้อย่างถูกต้องเข้ามาในการแสดงออก


1
ฉันได้รับสิ่งต่อไปนี้ในเครื่องของฉันซึ่งฉันไม่สามารถอัปเดตได้: sed: Not a recognized flag: i
javaPlease42

2
หืม มันยังเป็นบั๊กกี้ในแง่ที่ว่ามันจะลบ "t" s ต่อท้ายทั้งหมด :)
Good Person

2
"sed: ไม่ใช่ค่าสถานะที่รู้จัก: i -" สิ่งนี้เกิดขึ้นบน OSX คุณต้องเพิ่มส่วนขยายสำหรับไฟล์สำรองหลังจาก -i บน Macs เช่น: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo

1
@GoodPerson ถ้าคุณไม่ได้ล้อเล่นคุณอาจลืมที่จะหนีจากt:) \tเป็นแท็บสำหรับผู้ที่อาจยังไม่รู้
Sean Allred

2
@SeanAllred ไม่ได้ล้อเล่น: มันพังเต็มที่เว้นแต่คุณจะใช้ GNU sed (ซึ่งแตกด้วยวิธีอื่น ๆ มากมาย)
Good Person

59

อย่างน้อยใน Mountain Lion คำตอบของ Viktor จะลบอักขระ 't' เมื่ออยู่ท้ายบรรทัด ต่อไปนี้แก้ไขปัญหาดังกล่าว:

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

1
ฉันยังต้องการการ-Eระบุ "นิพจน์ทั่วไปแบบขยาย (สมัยใหม่)" ด้วย
Jared Beck

ทำงานได้อย่างมีเสน่ห์บน OS X ขอบคุณมาก
jww

1
คำตอบของ codaddict มีปัญหาเดียวกันบน OS X (ตอนนี้คือ macOS) นี่เป็นทางออกเดียวบนแพลตฟอร์มนี้
Franklin Yu

@JaredBeck เหมืองsedบน El Capitan ไม่ได้
Franklin Yu

19

ขอบคุณ codaddict ที่แนะนำ-iตัวเลือกนี้

คำสั่งต่อไปนี้ช่วยแก้ปัญหาใน Snow Leopard

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

ฉันพบสิ่งนี้ที่นี่joemaller.com/823/quick-note-about-seds-edit-in-place-option
Viktor

7
เช่นเดียวกับ @acrollet พูดว่าคุณไม่สามารถใช้\tกับ sed อื่น ๆ กว่า GNU sed tและจะได้รับการตีความว่าเป็นตัวอักษรตัวอักษร คำสั่งดูเหมือนจะใช้งานได้เท่านั้นอาจเป็นเพราะไม่มี TAB ในช่องว่างต่อท้ายหรือtท้ายประโยคในไฟล์ของคุณ ''ไม่แนะนำให้ใช้โดยไม่ระบุคำต่อท้ายสำรอง
Scrutinizer


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

1
เฮ้นั่นคือสิ่งที่ฉันต้องการ! โซลูชัน sed อื่น ๆ ที่โพสต์มีปัญหาในการผสานรวมกับการกำหนดตัวแปร piped (และ piped และ piped ... ) ในสคริปต์ทุบตีของฉัน แต่ของคุณทำงานนอกกรอบ
Eric L.

4

ฉันมีสคริปต์ใน. bashrc ของฉันที่ทำงานภายใต้ OSX และ Linux (ทุบตีเท่านั้น!)

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
}

ที่ฉันเพิ่ม:

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

สำหรับผู้ที่มองหาประสิทธิภาพ (ไฟล์จำนวนมากที่ต้องประมวลผลหรือไฟล์ขนาดใหญ่) การใช้ตัว+ดำเนินการซ้ำแทนที่จะ*ทำให้คำสั่งเร็วขึ้นกว่าสองเท่า

ด้วย GNU sed:

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

ฉันยังเปรียบเทียบอย่างรวดเร็วอย่างอื่น: การใช้[ \t]แทนการ[[:space:]]เร่งกระบวนการอย่างมีนัยสำคัญ (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

แค่เล่น ๆ:

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

ในกรณีที่เฉพาะเจาะจงตัวเลือกที่คนอื่นพูดถึงไปแล้วนั้นอยู่ห่างไกลsedจาก-iตัวเลือกที่ง่ายและมีความสุขที่สุด

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

Sponge อ่านอินพุตมาตรฐานและเขียนลงในไฟล์ที่ระบุ ซึ่งแตกต่างจากการเปลี่ยนเส้นทางเชลล์ฟองน้ำจะดูดซับอินพุตทั้งหมดก่อนที่จะเขียนไฟล์เอาต์พุต สิ่งนี้อนุญาตให้สร้างไปป์ไลน์ที่อ่านและเขียนไปยังไฟล์เดียวกัน

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


-1

หากต้องการตัดเฉพาะช่องว่าง (ในช่องว่างและแท็บกรณีของฉัน) จากบรรทัดที่มีอักขระที่ไม่ใช่ช่องว่างอย่างน้อยหนึ่งอักขระ (วิธีนี้จะไม่แตะเส้นเยื้องว่าง):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.