วิธีการตัดช่องว่างจากตัวแปร Bash?


920

ฉันมีเชลล์สคริปต์ด้วยรหัสนี้:

var=`hg st -R "$path"`
if [ -n "$var" ]; then
    echo $var
fi

แต่รหัสเงื่อนไขจะดำเนินการhg stเสมอเพราะพิมพ์อักขระบรรทัดใหม่อย่างน้อยหนึ่งตัว

  • มีวิธีง่ายๆในการตัดช่องว่างจาก$var(เช่นtrim()ในPHP )?

หรือ

  • มีวิธีมาตรฐานในการจัดการกับปัญหานี้หรือไม่?

ฉันสามารถใช้sedหรือAWKได้ แต่ฉันคิดว่ามันมีทางออกที่ดีกว่าสำหรับปัญหานี้


3
ที่เกี่ยวข้องหากคุณต้องการตัดพื้นที่บนจำนวนเต็มและรับจำนวนเต็มให้ห่อด้วย $ (($ var)) และยังสามารถทำเช่นนั้นได้เมื่ออยู่ในเครื่องหมายคำพูดคู่ สิ่งนี้กลายเป็นเรื่องสำคัญเมื่อฉันใช้คำสั่งวันที่และกับชื่อไฟล์
Volomike

"มีวิธีมาตรฐานในการจัดการกับปัญหานี้หรือไม่?" ใช่ใช้ [[แทน [] $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
มิตร

ถ้ามันช่วยได้อย่างน้อยตอนที่ฉันกำลังทดสอบบน Ubuntu 16.04 ใช้การจับคู่ไม้ขีดต่อไปนี้ในทุกวิถีทาง: echo " This is a string of char " | xargs. echo " This i's a string of char " | xargs -0แต่ถ้าคุณมีคำพูดเดียวในข้อความที่คุณสามารถทำต่อไปนี้: โปรดทราบว่าฉันพูดถึงล่าสุดของ xargs (4.6.0)
Luis Alvarado

เงื่อนไขไม่เป็นจริงเนื่องจากมีการขึ้นบรรทัดใหม่เมื่อ backticks กลืนขึ้นบรรทัดใหม่ล่าสุด สิ่งนี้จะไม่พิมพ์สิ่งใดtest=`echo`; if [ -n "$test" ]; then echo "Not empty"; fiแต่จะทำtest=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fiดังนั้นจึงต้องมีมากกว่าแค่บรรทัดใหม่ในตอนท้าย
Mecki

A = "123 4 5 6"; B = echo $A | sed -r 's/( )+//g';
bruziuz

คำตอบ:


1021

มานิยามตัวแปรที่มีช่องว่างนำหน้าต่อท้ายและช่องว่างกลาง:

FOO=' test test test '
echo -e "FOO='${FOO}'"
# > FOO=' test test test '
echo -e "length(FOO)==${#FOO}"
# > length(FOO)==16

วิธีลบช่องว่างทั้งหมด (แสดงโดย[:space:]ในtr):

FOO=' test test test '
FOO_NO_WHITESPACE="$(echo -e "${FOO}" | tr -d '[:space:]')"
echo -e "FOO_NO_WHITESPACE='${FOO_NO_WHITESPACE}'"
# > FOO_NO_WHITESPACE='testtesttest'
echo -e "length(FOO_NO_WHITESPACE)==${#FOO_NO_WHITESPACE}"
# > length(FOO_NO_WHITESPACE)==12

วิธีลบช่องว่างนำหน้า:

FOO=' test test test '
FOO_NO_LEAD_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//')"
echo -e "FOO_NO_LEAD_SPACE='${FOO_NO_LEAD_SPACE}'"
# > FOO_NO_LEAD_SPACE='test test test '
echo -e "length(FOO_NO_LEAD_SPACE)==${#FOO_NO_LEAD_SPACE}"
# > length(FOO_NO_LEAD_SPACE)==15

วิธีลบช่องว่างต่อท้ายเท่านั้น:

FOO=' test test test '
FOO_NO_TRAIL_SPACE="$(echo -e "${FOO}" | sed -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_TRAIL_SPACE='${FOO_NO_TRAIL_SPACE}'"
# > FOO_NO_TRAIL_SPACE=' test test test'
echo -e "length(FOO_NO_TRAIL_SPACE)==${#FOO_NO_TRAIL_SPACE}"
# > length(FOO_NO_TRAIL_SPACE)==15

วิธีลบช่องว่างนำหน้าและต่อท้าย - เชื่อมโยงseds:

FOO=' test test test '
FOO_NO_EXTERNAL_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_EXTERNAL_SPACE='${FOO_NO_EXTERNAL_SPACE}'"
# > FOO_NO_EXTERNAL_SPACE='test test test'
echo -e "length(FOO_NO_EXTERNAL_SPACE)==${#FOO_NO_EXTERNAL_SPACE}"
# > length(FOO_NO_EXTERNAL_SPACE)==14

หรือถ้า bash ของคุณรองรับคุณสามารถแทนที่echo -e "${FOO}" | sed ...ด้วยsed ... <<<${FOO}เช่น (สำหรับช่องว่างต่อท้าย):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"

63
ที่จะพูดคุยแก้ปัญหาในการจัดการกับทุกรูปแบบของช่องว่างแทนที่ตัวอักษรพื้นที่ในtrและคำสั่งด้วยsed [[:space:]]โปรดทราบว่าsedวิธีการนี้จะใช้งานได้กับอินพุตบรรทัดเดียวเท่านั้น สำหรับวิธีการที่ทำงานกับอินพุตหลายบรรทัดและใช้คุณสมบัติในตัวของ bash ให้ดูคำตอบโดย @bashfu และ @GuruM โซลูชันทั่วไปของ @Nicholas Sushkin รุ่นทั่วไปจะมีลักษณะเช่นนี้: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0

7
ถ้าคุณทำเช่นนั้นมักจะต่อท้ายalias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"ที่คุณ~/.profileช่วยให้คุณสามารถใช้และecho $SOMEVAR | trim cat somefile | trim
อินสแตนซ์ของฉัน

ฉันเขียนsedวิธีแก้ปัญหาที่ใช้เพียงนิพจน์เดียวมากกว่าสองข้อ: sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. มันตัดขอบชั้นนำและต่อท้ายช่องว่างและจับลำดับที่คั่นด้วยช่องว่างของอักขระที่ไม่ใช่ช่องว่างที่อยู่ตรงกลาง สนุก!
Victor Zamanian

@VictorZamanian วิธีแก้ปัญหาของคุณไม่ทำงานหากอินพุตมีช่องว่างเท่านั้น โซลูชัน sed สองรูปแบบที่ MattyV มอบให้และฉันทำงานได้ดีกับอินพุตช่องว่างเท่านั้น
Torben

@Torben จุดยุติธรรม ฉันคิดว่าการแสดงออกเดียวสามารถทำตามเงื่อนไขด้วย|เพื่อที่จะให้มันเป็นหนึ่งการแสดงออกเดียวมากกว่าหลาย
Victor Zamanian

965

คำตอบง่ายๆคือ:

echo "   lol  " | xargs

xargsจะทำการตัดแต่งให้คุณ มันเป็นหนึ่งในคำสั่ง / โปรแกรมไม่มีพารามิเตอร์ส่งคืนสตริงที่ถูกตัดง่าย ๆ !

หมายเหตุ: นี่จะไม่ลบช่องว่างภายในทั้งหมด "foo bar"เหมือนเดิม "foobar"มันไม่ได้กลายเป็น แต่ช่องว่างหลายจะข้นไปที่ช่องว่างเดียวดังนั้นจะกลายเป็น"foo bar" "foo bar"นอกจากนี้มันจะไม่ลบอักขระจุดสิ้นสุดของบรรทัด


27
ดี มันใช้งานได้ดีจริงๆ ฉันได้ตัดสินใจที่จะใช้ท่อเพื่อxargs echoให้ verbose เกี่ยวกับสิ่งที่ฉันทำ แต่ xargs ด้วยตัวเองจะใช้ echo ตามค่าเริ่มต้น
จะ

24
เคล็ดลับดี แต่ระวังคุณสามารถใช้มันสำหรับสตริงหนึ่งบรรทัด แต่โดย xargs design มันจะไม่เพียงแค่ทำการตัดแต่งด้วยเนื้อหา piped หลายบรรทัด sed เป็นเพื่อนของคุณแล้ว
Jocelyn delalande

22
ปัญหาเฉพาะกับ xargs คือมันจะแนะนำการขึ้นบรรทัดใหม่ถ้าคุณต้องการที่จะขึ้นบรรทัดใหม่ฉันขอแนะนำให้sed 's/ *$//'เป็นทางเลือก คุณสามารถเห็นการขึ้นxargsบรรทัดใหม่ดังนี้: echo -n "hey thiss " | xargs | hexdump คุณจะสังเกตเห็น0a73ว่าaเป็น newline หากคุณทำเช่นเดียวกันกับsed: echo -n "hey thiss " | sed 's/ *$//' | hexdumpคุณจะเห็น0073ไม่มีการขึ้นบรรทัดใหม่

8
ระวัง; สิ่งนี้จะแตกหักถ้าสตริงการ xargs มีช่องว่างส่วนเกินในระหว่าง ชอบ "นี่คือหนึ่งอาร์กิวเมนต์" xargs จะแบ่งออกเป็นสี่
bos

64
นี้ไม่ดี. 1. มันจะเปิดเข้าไปa<space><space>b a<space>b2. ยิ่งขึ้นมันจะเปิดเข้าไปa"b"c'd'e abcde3. ยิ่งกว่านั้น: มันจะล้มเหลวa"bเป็นต้น
Sasha

357

มีวิธีแก้ปัญหาซึ่งใช้ Bash บิวด์อินที่เรียกว่าไวด์การ์ด :

var="    abc    "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"   
printf '%s' "===$var==="

นี่คือฟังก์ชั่นห่อหุ้มเดียวกัน:

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   
    printf '%s' "$var"
}

คุณผ่านสายอักขระที่จะตัดแต่งในแบบฟอร์มที่ยกมา เช่น:

trim "   abc   "

สิ่งที่ดีเกี่ยวกับโซลูชันนี้คือมันจะทำงานกับเชลล์ที่สอดคล้องกับ POSIX

การอ้างอิง


17
ฉลาด! นี่เป็นโซลูชันที่ฉันโปรดปรานเนื่องจากใช้ฟังก์ชันการทุบตีในตัว ขอบคุณสำหรับการโพสต์! @San มันเป็นสองสายจดจ้องซ้อน เช่นจะตัดทุกอย่างจากท้ายที่สุดกลับชั้นนำs=" 1 2 3 "; echo \""${s%1 2 3 }"\" " "ซับมิด1 2 3 ด้วย[![:space:]]*บอกให้ "ค้นหาตัวละครที่ไม่ใช่ช่องว่างก่อนแล้วปิดบังมันและทุกอย่างหลังจากนั้น" การใช้%%แทนที่จะ%ทำให้การดำเนินการตัดแต่งจากความโลภ สิ่งนี้ซ้อนอยู่ในการตัดแต่งแบบไม่โลภตั้งแต่เริ่มต้นดังนั้นคุณตัดแต่งตั้ง" "แต่เริ่มต้น จากนั้นสลับ%, # และ * สำหรับช่องว่างท้าย ปัง!
Mark G.

2
ฉันไม่พบผลข้างเคียงใด ๆ ที่ไม่ต้องการและรหัสหลักใช้ได้กับเชลล์แบบ POSIX อื่น ๆ ด้วย อย่างไรก็ตามภายใต้ Solaris 10 จะไม่ทำงานกับ/bin/sh(เฉพาะกับ/usr/xpg4/bin/shแต่นี่ไม่ใช่สิ่งที่จะใช้กับสคริปต์ sh ปกติ)
vinc17

9
ทางออกที่ดีกว่าการใช้ sed, tr ฯลฯ เนื่องจากเร็วกว่ามากหลีกเลี่ยงการใช้ส้อม () เกี่ยวกับ Cygwin ความแตกต่างในความเร็วคือคำสั่งของขนาด
ยีน Pavlovsky

9
@San ในตอนแรกฉันถูกนิ่งงันเพราะฉันคิดว่าสิ่งเหล่านี้เป็นการแสดงออกปกติ พวกเขาจะไม่. แต่นี่คือไวยากรณ์การจับคู่รูปแบบ ( gnu.org/software/bash/manual/html_node/Pattern-Matching.html , wiki.bash-hackers.org/syntax/pattern ) ที่ใช้ในการกำจัดซับสตริง ( tldp.org/LDP/abs) /html/string-manipulation.html ) ดังนั้น${var%%[![:space:]]*}พูดว่า "ลบออกจากvarสตริงย่อยที่ยาวที่สุดที่ขึ้นต้นด้วยอักขระที่ไม่ใช่ช่องว่าง" นั่นหมายความว่าคุณจะเหลือเฉพาะกับพื้นที่ชั้นนำ (s) ${var#..ซึ่งคุณต่อมาลบด้วย บรรทัดต่อไปนี้ (ต่อท้าย) อยู่ตรงข้าม
Ohad Schneider

8
นี่เป็นทางออกที่ดีเลิศ ฟอร์กหนึ่งหรือภายนอกมากขึ้นกระบวนการ (เช่นawk, sed, tr, xargs) เพียงเพื่อตัดช่องว่างจากสายเดียวคือบ้าพื้นฐาน - โดยเฉพาะอย่างยิ่งเมื่อเปลือกหอย (รวมถึงทุบตี) แล้วให้สตริงพื้นเมืองของสิ่งอำนวยความสะดวก munging ออกจากกล่อง
เซซิลแกงกะหรี่

81

Bash มีคุณสมบัติที่เรียกว่าการขยายพารามิเตอร์ซึ่งเหนือสิ่งอื่นใดช่วยให้การแทนที่สตริงตามรูปแบบที่เรียกว่า(รูปแบบคล้ายกับนิพจน์ทั่วไป แต่มีความแตกต่างและข้อ จำกัด พื้นฐาน) [บรรทัดแรกของ flussence: Bash มีการแสดงออกปกติ แต่มันซ่อนอยู่อย่างดี:]

ข้อมูลต่อไปนี้สาธิตวิธีการลบพื้นที่สีขาวทั้งหมด (แม้จากภายใน) ออกจากค่าตัวแปร

$ var='abc def'
$ echo "$var"
abc def
# Note: flussence's original expression was "${var/ /}", which only replaced the *first* space char., wherever it appeared.
$ echo -n "${var//[[:space:]]/}"
abcdef

2
หรือค่อนข้างจะใช้สำหรับช่องว่างที่อยู่ตรงกลางของ var แต่ไม่ใช่เมื่อฉันพยายามที่จะยึดไว้ที่ท้าย
Paul Tomblin

สิ่งนี้ช่วยอะไรบ้าง จาก manpage: "$ {พารามิเตอร์ / รูปแบบ / สตริง} [... ] หากรูปแบบเริ่มต้นด้วย% รูปแบบนั้นจะต้องจับคู่ที่ส่วนท้ายของค่าพารามิเตอร์ที่ขยายออก"

@ ดังนั้นพวกเขาไม่ได้แสดงออกปกติจริงๆ แต่สิ่งที่คล้ายกัน?
พอลทอมบลิน

3
พวกเขากำลัง regex เพียงภาษาแปลก ๆ

13
${var/ /}ลบอักขระช่องว่างแรก ${var// /}ลบอักขระช่องว่างทั้งหมด ไม่มีวิธีใดที่จะตัดเฉพาะส่วนที่นำหน้าและต่อท้ายช่องว่างด้วยโครงสร้างนี้เท่านั้น
Gilles 'ดังนั้นหยุดความชั่วร้าย'

60

เพื่อที่จะลบช่องว่างทั้งหมดจากจุดเริ่มต้นและจุดสิ้นสุดของสตริง (รวมถึงตัวอักษรท้ายบรรทัด):

echo $variable | xargs echo -n

วิธีนี้จะลบช่องว่างซ้ำซ้อนด้วย:

echo "  this string has a lot       of spaces " | xargs echo -n

สร้าง: 'สตริงนี้มีช่องว่างมากมาย'


5
โดยทั่วไป xargs จะลบตัวคั่นทั้งหมดออกจากสตริง โดยค่าเริ่มต้นจะใช้พื้นที่เป็นตัวคั่น (ซึ่งอาจมีการเปลี่ยนแปลงโดยตัวเลือก -d)
rkachach

4
นี่เป็นวิธีการแก้ปัญหาที่สั้นที่สุด (ทั้งสั้นและอ่านง่าย)
Potherca

ทำไมคุณต้องการecho -nเลย echo " my string " | xargsมีเอาต์พุตเดียวกัน
bfontaine

echo -n ลบจุดสิ้นสุดของบรรทัดด้วย
rkachach

55

แถบหนึ่งนำและพื้นที่ต่อท้ายหนึ่ง

trim()
{
    local trimmed="$1"

    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"

    echo "$trimmed"
}

ตัวอย่างเช่น:

test1="$(trim " one leading")"
test2="$(trim "one trailing ")"
test3="$(trim " one leading and one trailing ")"
echo "'$test1', '$test2', '$test3'"

เอาท์พุท:

'one leading', 'one trailing', 'one leading and one trailing'

ดึงทุกพื้นที่ชั้นนำและต่อท้าย

trim()
{
    local trimmed="$1"

    # Strip leading spaces.
    while [[ $trimmed == ' '* ]]; do
       trimmed="${trimmed## }"
    done
    # Strip trailing spaces.
    while [[ $trimmed == *' ' ]]; do
        trimmed="${trimmed%% }"
    done

    echo "$trimmed"
}

ตัวอย่างเช่น:

test4="$(trim "  two leading")"
test5="$(trim "two trailing  ")"
test6="$(trim "  two leading and two trailing  ")"
echo "'$test4', '$test5', '$test6'"

เอาท์พุท:

'two leading', 'two trailing', 'two leading and two trailing'

9
สิ่งนี้จะตัดเฉพาะอักขระเว้นวรรค 1 ตัว เสียงก้องส่งผลให้'hello world ', 'foo bar', 'both sides '
Joe

@ Joe ฉันได้เพิ่มตัวเลือกที่ดีกว่า
wjandrea

42

จากส่วน Bash Guide บนglobbing

หากต้องการใช้ extglob ในการขยายพารามิเตอร์

 #Turn on extended globbing  
shopt -s extglob  
 #Trim leading and trailing whitespace from a variable  
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}  
 #Turn off extended globbing  
shopt -u extglob  

นี่คือฟังก์ชั่นเดียวกับที่ห่อหุ้มอยู่ในฟังก์ชั่น (หมายเหตุ: จำเป็นต้องอ้างถึงสตริงอินพุตที่ส่งผ่านไปยังฟังก์ชัน):

trim() {
    # Determine if 'extglob' is currently on.
    local extglobWasOff=1
    shopt extglob >/dev/null && extglobWasOff=0 
    (( extglobWasOff )) && shopt -s extglob # Turn 'extglob' on, if currently turned off.
    # Trim leading and trailing whitespace
    local var=$1
    var=${var##+([[:space:]])}
    var=${var%%+([[:space:]])}
    (( extglobWasOff )) && shopt -u extglob # If 'extglob' was off before, turn it back off.
    echo -n "$var"  # Output trimmed string.
}

การใช้งาน:

string="   abc def ghi  ";
#need to quote input-string to preserve internal white-space if any
trimmed=$(trim "$string");  
echo "$trimmed";

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

trim() {
    shopt -s extglob
    set -- "${1##+([[:space:]])}"
    printf "%s" "${1%%+([[:space:]])}" 
}

ดังนั้น:

$ s=$'\t\n \r\tfoo  '
$ shopt -u extglob
$ shopt extglob
extglob         off
$ printf ">%q<\n" "$s" "$(trim "$s")"
>$'\t\n \r\tfoo  '<
>foo<
$ shopt extglob
extglob         off

2
ดังที่คุณได้สังเกตเห็นการตัดแต่ง () ลบช่องว่างนำหน้าและต่อท้ายเท่านั้น
GuruM

ในฐานะที่เป็น mkelement ได้ระบุไว้แล้วคุณจะต้องผ่านพารามิเตอร์ฟังก์ชั่นเป็นสตริงที่ยกมาเช่น $ (ตัด "$ string") แทน $ (ตัดแต่ง $ string) ฉันได้อัปเดตรหัสเพื่อแสดงการใช้งานที่ถูกต้อง ขอบคุณ
GuruM

เท่าที่ฉันซาบซึ้งที่ทราบเกี่ยวกับตัวเลือกของเชลล์ฉันไม่คิดว่าผลลัพธ์ที่ได้จะออกมาสง่างามไปกว่าการใช้การแทนที่ลวดลาย 2 แบบ
sehe

โปรดทราบว่า (ด้วยรุ่นล่าสุดของ Bash เพียงพอ) คุณสามารถลดความซับซ้อนของกลไกในการกู้คืนตัวเลือกextglobโดยใช้shopt -p: เพียงแค่เขียนlocal restore="$(shopt -p extglob)" ; shopt -s extglobที่จุดเริ่มต้นของฟังก์ชั่นของคุณและeval "$restore"ในตอนท้าย
Maëlan

สุดยอดทางออก! การปรับปรุงที่มีศักยภาพอย่างหนึ่ง: ดูเหมือนว่า[[:space:]]อาจถูกแทนที่ด้วยพื้นที่${var##+( )}และ${var%%+( )}ทำงานได้ดีและอ่านง่ายขึ้น
DKroot

40

คุณสามารถตัดแต่งได้ง่ายๆด้วยecho:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

สิ่งนี้จะยุบช่องว่างที่อยู่ติดกันหลายแห่งไว้ในที่เดียว
Evgeni Sergeev

7
คุณลองใช้เมื่อfooมีอักขระตัวแทนหรือไม่ เช่นfoo=" I * have a wild card"... แปลกใจ! ยิ่งไปกว่านั้นสิ่งนี้จะยุบช่องว่างที่ต่อเนื่องกันหลาย ๆ
gniourf_gniourf

5
นี่เป็นวิธีแก้ปัญหาที่ยอดเยี่ยมหากคุณ: 1. ไม่ต้องการช่องว่างที่ปลาย 2. ต้องการช่องว่างเดียวระหว่างแต่ละคำ 3. ทำงานกับอินพุตที่ควบคุมโดยไม่มีสัญลักษณ์แทน โดยพื้นฐานแล้วจะเปลี่ยนรายการที่จัดรูปแบบไม่ดีเป็นรายการที่ดี
musicin3d

เตือนความจำดี ๆ ของสัญลักษณ์แทน @gniourf_gniourf +1 ยังเป็นทางออกที่ยอดเยี่ยม Vamp +1 ให้คุณด้วย
ดร. เบคโก

25

ฉันมักจะทำมันด้วยความใจเย็น

  var=`hg st -R "$path" | sed -e 's/  *$//'`

หากมีวิธีแก้ปัญหาที่สวยงามกว่านี้ฉันหวังว่าจะมีคนโพสต์ไว้


คุณสามารถอธิบายไวยากรณ์ได้sedหรือไม่
farid99

2
การแสดงออกปกติตรงกับช่องว่างต่อท้ายทั้งหมดและแทนที่ด้วยไม่มีอะไร
พอลทอมบลิน

4
ช่องว่างชั้นนำล่ะ
Qian Chen

sed -e 's/\s*$//'แถบนี้ทุกช่องว่างต่อท้าย คำอธิบาย: 's' หมายถึงการค้นหา, '\ s' หมายถึงช่องว่างทั้งหมด, '*' หมายถึงศูนย์หรือจำนวนมาก, '$' หมายถึงจนถึงจุดสิ้นสุดของบรรทัดและ '//' หมายถึงแทนที่ทั้งหมดที่ตรงกันด้วยสตริงว่าง .
Craig

ใน 's / * $ //' ทำไมมี 2 ช่องว่างก่อนเครื่องหมายดอกจันแทนที่จะเว้นหนึ่งช่อง นั่นคือการพิมพ์ผิดหรือเปล่า?
Brent212


24

ด้วยคุณสมบัติการจับคู่รูปแบบเพิ่มเติมของ Bash ที่เปิดใช้งาน ( shopt -s extglob) คุณสามารถใช้สิ่งนี้:

{trimmed##*( )}

เพื่อลบช่องว่างนำจำนวนเท่าใดก็ได้


ยอดเยี่ยม! ฉันคิดว่านี่เป็นทางออกที่เบาและสง่างามที่สุด
dubiousjim

1
ดูโพสต์ของ @ GuruM ด้านล่างสำหรับวิธีแก้ปัญหาที่คล้ายกัน แต่มีมากกว่าทั่วไปที่ (a) เกี่ยวข้องกับพื้นที่สีขาวทุกรูปแบบและ (b) จัดการพื้นที่สีขาวตามท้าย
mklement0

@mkelement +1 สำหรับการแก้ไขปัญหาในการเขียนข้อมูลโค้ดของฉันเป็นฟังก์ชัน ขอบคุณ
GuruM

ทำงานร่วมกับค่าเริ่มต้น / bin / ksh ของ OpenBSD เช่นกัน /bin/sh -o posixทำงานเกินไป แต่ฉันก็สงสัย
Clint Pachl

ไม่ใช่ตัวช่วยสร้างการทุบตีที่นี่; อะไรนะtrimmed? มันเป็นสิ่งในตัวหรือตัวแปรที่ถูกตัดแต่ง?
Abhijit Sarkar

19
# Trim whitespace from both ends of specified parameter

trim () {
    read -rd '' $1 <<<"${!1}"
}

# Unit test for trim()

test_trim () {
    local foo="$1"
    trim foo
    test "$foo" = "$2"
}

test_trim hey hey &&
test_trim '  hey' hey &&
test_trim 'ho  ' ho &&
test_trim 'hey ho' 'hey ho' &&
test_trim '  hey  ho  ' 'hey  ho' &&
test_trim $'\n\n\t hey\n\t ho \t\n' $'hey\n\t ho' &&
test_trim $'\n' '' &&
test_trim '\n' '\n' &&
echo passed

2
! ที่น่าตื่นตาตื่นใจ ง่ายและมีประสิทธิภาพ! เห็นได้ชัดว่าทางออกที่ฉันชื่นชอบ ขอบคุณ!
xebeche

1
@CraigMcQueen มันเป็นค่าตัวแปรตามที่readจะเก็บไว้ที่ตัวแปรตามชื่อ $ 1 a รุ่นที่ถูกตัดทอนมูลค่า $ {! 1}
Aquarius Power

2
พารามิเตอร์ของฟังก์ชัน trim () เป็นชื่อตัวแปร: ดูการเรียกเพื่อตัด () ภายใน test_trim () ภายใน trim () ตามที่เรียกจาก test_trim (), $ 1 ขยายเป็น foo และ $ {! 1} ขยายเป็น $ foo (นั่นคือไปที่เนื้อหาปัจจุบันของตัวแปร foo) ค้นหาคู่มือทุบตีสำหรับ 'ตัวแปรทางอ้อม'
flabdablet

1
วิธีการแก้ไขเล็กน้อยนี้เพื่อรองรับการตัดแต่งหลาย vars ในการโทรครั้งเดียว? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
ยีน Pavlovsky

2
@ AquariusPower ไม่จำเป็นต้องใช้ echo ใน subshell สำหรับซับในเดียวread -rd '' str <<<"$str"ก็ทำได้
flabdablet

12

มีคำตอบมากมาย แต่ฉันก็ยังเชื่อว่าสคริปต์ที่เพิ่งเขียนของฉันมีค่าควรถูกกล่าวถึงเพราะ:

  • ถูกทดสอบสำเร็จในเชลล์ bash / dash / busybox shell
  • มันมีขนาดเล็กมาก
  • มันไม่ได้ขึ้นอยู่กับคำสั่งภายนอกและไม่จำเป็นต้องแยก (-> การใช้ทรัพยากรอย่างรวดเร็วและต่ำ)
  • มันทำงานได้ตามที่คาดไว้:
    • มันแถบทุกพื้นที่และแท็บจากจุดเริ่มต้นและจุดสิ้นสุด แต่ไม่
    • สำคัญ: มันจะไม่ลบสิ่งใดออกจากกลางสตริง (มีคำตอบอื่น ๆ อีกมากมาย) แม้จะขึ้นบรรทัดใหม่
    • พิเศษ: การ"$*"รวมหลายอาร์กิวเมนต์โดยใช้หนึ่งช่องว่าง หากคุณต้องการตัดและแสดงเฉพาะอาร์กิวเมนต์แรกให้ใช้"$1"แทน
    • หากไม่มีปัญหาใด ๆ กับรูปแบบชื่อไฟล์ที่ตรงกัน ฯลฯ

สคริปต์:

trim() {
  local s2 s="$*"
  until s2="${s#[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

การใช้งาน:

mystring="   here     is
    something    "
mystring=$(trim "$mystring")
echo ">$mystring<"

เอาท์พุท:

>here     is
    something<

Bah ใน C นี่จะเป็นวิธีที่ง่ายกว่าในการนำไปใช้!
นิลส์

แน่ใจ น่าเสียดายที่นี่ไม่ใช่ C และบางครั้งคุณต้องการหลีกเลี่ยงการเรียกเครื่องมือภายนอก
Daniel Alder

ในการทำให้โค้ดอ่านได้ง่ายขึ้นและใช้งานได้กับการคัดลอกในอดีตคุณสามารถเปลี่ยนวงเล็บเป็นอักขระที่หลบหนี:[\ \t]
leondepeon

@leondepeon คุณลองหรือไม่ ฉันพยายามเมื่อฉันเขียนมันและลองอีกครั้งและข้อเสนอแนะของคุณไม่ทำงานในการทุบตี, เส้นประ, busybox ใด ๆ
Daniel Alder

@DanielAlder ฉันทำ แต่เมื่อ 3 ปีที่แล้วฉันไม่พบรหัสที่ฉันใช้ อย่างไรก็ตามในตอนนี้ฉันอาจใช้[[:space:]]คำตอบเดียวในคำตอบอื่น: stackoverflow.com/a/3352015/3968618
leondepeon

11

trคุณสามารถใช้โรงเรียนเก่า ตัวอย่างเช่นสิ่งนี้จะคืนค่าจำนวนไฟล์ที่ถูกแก้ไขในที่เก็บ git ซึ่ง whitespaces ถูกปล้น

MYVAR=`git ls-files -m|wc -l|tr -d ' '`

1
นี่ไม่ได้ตัดช่องว่างจากด้านหน้าและด้านหลัง แต่จะลบช่องว่างทั้งหมดออกจากสตริง
นิค

11

สิ่งนี้ใช้ได้กับฉัน:

text="   trim my edges    "

trimmed=$text
trimmed=${trimmed##+( )} #Remove longest matching series of spaces from the front
trimmed=${trimmed%%+( )} #Remove longest matching series of spaces from the back

echo "<$trimmed>" #Adding angle braces just to make it easier to confirm that all spaces are removed

#Result
<trim my edges>

หากต้องการวางลงบนบรรทัดที่น้อยกว่าสำหรับผลลัพธ์เดียวกัน:

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}

1
ไม่ได้ผลสำหรับฉัน คนแรกที่พิมพ์สตริงที่ไม่ได้รับการดัดแปลง การโยนครั้งที่สองเป็นการเปลี่ยนตัวที่ไม่ดี คุณช่วยอธิบายสิ่งที่เกิดขึ้นที่นี่ได้ไหม?
musicin3d

1
@ musicin3d: นี่เป็นเว็บไซต์ที่ฉันใช้บ่อยซึ่งจะอธิบายการทำงานของการปรับเปลี่ยนตัวแปรในการค้นหาทุบตี${var##Pattern}เพื่อดูรายละเอียดเพิ่มเติม นอกจากนี้เว็บไซต์นี้จะอธิบายถึงรูปแบบการทุบตี ดังนั้น##วิธีการลบรูปแบบที่กำหนดจากด้านหน้าและ%%หมายถึงลบรูปแบบที่กำหนดจากด้านหลัง +( )ส่วนคือรูปแบบและมันหมายถึง "หนึ่งหรือเกิดขึ้นมากขึ้นของพื้นที่"
gMale

ตลกมันทำงานในพรอมต์ แต่ไม่ใช่หลังจากเปลี่ยนไปใช้ไฟล์สคริปต์ทุบตี
ดร. เบคโก

แปลก. มันเป็นเวอร์ชั่นทุบตีเดียวกันในทั้งสองกรณี?
gMale

11
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

หรือ

# Strip leading white space (new line inclusive).
ltrim(){
    [[ "$1" =~ [^[:space:]].* ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    [[ "$1" =~ .*[^[:space:]] ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

หรือ

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

หรือ

# Strip leading specified characters.  ex: str=$(ltrim "$str" $'\n a')
ltrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"]) ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip trailing specified characters.  ex: str=$(rtrim "$str" $'\n a')
rtrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1" "$2")" "$2")"
}

หรือ

อาคารที่สร้างจากจิตวิญญาณแห่งมอสกิต ...

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$"`"
}

หรือ

# Strip leading white space (new line inclusive).
ltrim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)"`"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    printf "%s" "`expr "$1" : "^\(.*[^[:space:]]\)[[:space:]]*$"`"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

8

ฉันเคยเห็นสคริปต์ใช้การกำหนดตัวแปรเพื่อทำงาน:

$ xyz=`echo -e 'foo \n bar'`
$ echo $xyz
foo bar

ช่องว่างจะรวมกันและตัดโดยอัตโนมัติ หนึ่งจะต้องระวังของเชลล์เมตาอักขระ (ความเสี่ยงที่อาจฉีด)

ฉันยังจะแนะนำเสมอแทนสองตัวแปรข้อความในเงื่อนไขเชลล์:

if [ -n "$var" ]; then

เนื่องจากบางอย่างเช่น -o หรือเนื้อหาอื่น ๆ ในตัวแปรสามารถแก้ไขข้อโต้แย้งการทดสอบของคุณได้


3
มันคือการใช้ unquoted ของ$xyzกับechoที่ไม่ coalescing ช่องว่างที่ไม่ได้กำหนดตัวแปร xyz=$(echo -n $xyz)ในการจัดเก็บค่าตัดแต่งในตัวแปรในตัวอย่างของคุณคุณจะต้องใช้ นอกจากนี้วิธีนี้ยังขึ้นอยู่กับการขยายชื่อพา ธ ที่ไม่พึงประสงค์
mklement0

นี่คือ jut ผิดค่าในxyzตัวแปรจะไม่ถูกตัดแต่ง
caesarsol

7
var='   a b c   '
trimmed=$(echo $var)

1
นั่นจะไม่ทำงานหากมีมากกว่าหนึ่งช่องว่างระหว่างคำสองคำใด ๆ ลอง: echo $(echo "1 2 3")(มีช่องว่างสองช่องระหว่าง 1, 2 และ 3)
joshlf

7

ฉันแค่ใช้ sed:

function trim
{
    echo "$1" | sed -n '1h;1!H;${;g;s/^[ \t]*//g;s/[ \t]*$//g;p;}'
}

a) ตัวอย่างการใช้งานบนสตริงบรรทัดเดียว

string='    wordA wordB  wordC   wordD    '
trimmed=$( trim "$string" )

echo "GIVEN STRING: |$string|"
echo "TRIMMED STRING: |$trimmed|"

เอาท์พุท:

GIVEN STRING: |    wordA wordB  wordC   wordD    |
TRIMMED STRING: |wordA wordB  wordC   wordD|

b) ตัวอย่างการใช้งานกับสตริงหลายบรรทัด

string='    wordA
   >wordB<
wordC    '
trimmed=$( trim "$string" )

echo -e "GIVEN STRING: |$string|\n"
echo "TRIMMED STRING: |$trimmed|"

เอาท์พุท:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) หมายเหตุสุดท้าย:
หากคุณไม่ต้องการใช้ฟังก์ชั่นสำหรับสตริงบรรทัดเดียวคุณสามารถใช้คำสั่ง "ง่ายต่อการจดจำ" เช่น:

echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

ตัวอย่าง:

echo "   wordA wordB wordC   " | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

เอาท์พุท:

wordA wordB wordC

การใช้สตริงด้านบนแบบหลายบรรทัดด้านบนจะใช้งานได้เช่นกันแต่โปรดทราบว่ามันจะตัดส่วนต่อท้าย / นำหน้าหลาย ๆ ช่องว่างภายในเช่นเดียวกับที่ GuruM สังเกตเห็นในความคิดเห็น

string='    wordAA
    >four spaces before<
 >one space before<    '
echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

เอาท์พุท:

wordAA
>four spaces before<
>one space before<

ดังนั้นถ้าคุณตั้งใจจะรักษาช่องว่างเหล่านั้นโปรดใช้ฟังก์ชั่นที่จุดเริ่มต้นของคำตอบของฉัน!

d) คำอธิบายของไวยากรณ์ sed "ค้นหาและแทนที่" บนสตริงหลายบรรทัดที่ใช้ในการตัดแต่งฟังก์ชั่น:

sed -n '
# If the first line, copy the pattern to the hold buffer
1h
# If not the first line, then append the pattern to the hold buffer
1!H
# If the last line then ...
$ {
    # Copy from the hold to the pattern buffer
    g
    # Do the search and replace
    s/^[ \t]*//g
    s/[ \t]*$//g
    # print
    p
}'

หมายเหตุ: ตามที่แนะนำโดย @mkelement มันจะไม่ทำงานสำหรับสตริงหลายบรรทัดแม้ว่ามันควรจะทำงานกับสตริงบรรทัดเดียว
GuruM

1
คุณผิด: มันทำงานบนสายหลายสายได้เช่นกัน ลองทดสอบดูสิ :)
Luca Borrione

+1 สำหรับการใช้งาน - ทำให้ฉันทดสอบโค้ดได้ง่าย อย่างไรก็ตามรหัสยังคงใช้งานไม่ได้กับสตริงแบบหลายบรรทัด หากคุณดูที่เอาต์พุตอย่างระมัดระวังคุณจะสังเกตเห็นว่าช่องว่างภายในใด ๆ ที่นำหน้า / ต่อท้ายจะถูกลบเช่นช่องว่างด้านหน้า "มัลติไลน์" จะถูกแทนที่ด้วย "มัลติไลน์" เพียงลองเพิ่มจำนวนช่องว่างนำหน้า / ต่อท้ายในแต่ละบรรทัด
GuruM

ตอนนี้ฉันเห็นสิ่งที่คุณหมายถึง! ขอบคุณสำหรับหัวขึ้นฉันแก้ไขคำตอบของฉัน
Luca Borrione

@ "Luca Borrione" - ยินดีต้อนรับ :-) คุณช่วยอธิบายซินแทกซ์ที่คุณใช้ในการตกแต่ง () ได้ไหม นอกจากนี้ยังอาจช่วยให้ผู้ใช้รหัสของคุณบิดไปใช้งานอื่น ๆ นอกจากนี้มันยังอาจช่วยค้นหา edge-cases สำหรับนิพจน์ทั่วไป
GuruM

6

นี่คือฟังก์ชั่นการตัดแต่ง () ที่จดจ้องและทำให้ช่องว่างเป็นปกติ

#!/bin/bash
function trim {
    echo $*
}

echo "'$(trim "  one   two    three  ")'"
# 'one two three'

และอีกตัวแปรหนึ่งที่ใช้นิพจน์ทั่วไป

#!/bin/bash
function trim {
    local trimmed="$@"
    if [[ "$trimmed" =~ " *([^ ].*[^ ]) *" ]]
    then 
        trimmed=${BASH_REMATCH[1]}
    fi
    echo "$trimmed"
}

echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'

วิธีแรกนั้นมีความยุ่งยากในการที่มันไม่เพียง แต่ทำให้ช่องว่างภายในเป็นปกติ (แทนที่พื้นที่ภายในทั้งหมดของช่องว่างด้วยช่องว่างแต่ละช่องเท่านั้น) แต่ยังต้องกลมกลืน (การขยายชื่อพา ธ ) ดังนั้นตัวอย่างเช่น*อักขระในสตริงอินพุตจะ ขยายไปยังไฟล์และโฟลเดอร์ทั้งหมดในโฟลเดอร์การทำงานปัจจุบัน สุดท้ายถ้าตั้งค่า $ IFS เป็นค่าที่ไม่ใช่ค่าเริ่มต้นการตัดแต่งอาจไม่ทำงาน (แม้ว่าจะแก้ไขได้อย่างง่ายดายด้วยการเพิ่มlocal IFS=$' \t\n') การตัดแต่ง จำกัด เฉพาะรูปแบบช่องว่างต่อไปนี้: ช่องว่าง\tและ\nอักขระ
mklement0

1
วิธีที่สองที่ใช้นิพจน์ทั่วไปนั้นยอดเยี่ยมและไม่มีผลข้างเคียง แต่ในรูปแบบปัจจุบันนั้นมีปัญหา: (a) กับ bash v3.2 + การจับคู่จะโดยค่าเริ่มต้นไม่ทำงานเนื่องจากนิพจน์ทั่วไปต้องไม่เป็นไปตามลำดับ ในการทำงานและ (b) นิพจน์ทั่วไปนั้นไม่ได้จัดการกับกรณีที่สตริงอินพุตเป็นอักขระตัวเดียวที่ไม่ใช่ช่องว่างที่ล้อมรอบด้วยช่องว่าง เพื่อแก้ไขปัญหาเหล่านี้แทนที่สอดคล้องกับ:if if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]ในที่สุดวิธีการจะจัดการเฉพาะกับช่องว่างไม่ใช่ช่องว่างรูปแบบอื่น ๆ (ดูความคิดเห็นถัดไปของฉัน)
mklement0

2
ฟังก์ชั่นที่ใช้การแสดงออกปกติเกี่ยวข้องเฉพาะกับช่องว่างและไม่ใช่ช่องว่างในรูปแบบอื่น ๆ แต่มันง่ายที่จะพูดคุยทั่วไป: แทนที่ifบรรทัดด้วย:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0

6

ใช้ AWK:

echo $var | awk '{gsub(/^ +| +$/,"")}1'

น่ารักที่ใช้งานได้ (เช่น :) $stripped_version=echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1'``
rogerdpack

4
ยกเว้น awk ไม่ได้ทำอะไรเลย: echo'ing ตัวแปร unquote ได้ตัดช่องว่างออกไปแล้ว
glenn jackman

6

การมอบหมายไม่สนใจช่องว่างนำหน้าและต่อท้ายและสามารถใช้ในการตัด:

$ var=`echo '   hello'`; echo $var
hello

8
ที่ไม่เป็นความจริง. เป็น "เสียงสะท้อน" ที่ลบช่องว่างออกไม่ใช่งานที่มอบหมาย ในตัวอย่างของคุณให้ทำecho "$var"เพื่อดูค่าด้วยช่องว่าง
Nicholas Sushkin

2
@NicholasSushkin One สามารถทำได้var=$(echo $var)แต่ฉันไม่แนะนำ โซลูชันอื่น ๆ ที่นำเสนอที่นี่เป็นที่ต้องการ
xebeche

5

สิ่งนี้ไม่มีปัญหากับการวนรอบที่ไม่ต้องการเช่นกันพื้นที่สีขาวภายในยังไม่ได้รับการแก้ไข (สมมติว่า$IFSถูกตั้งค่าเป็นค่าเริ่มต้นซึ่งก็คือ' \t\n')

มันขึ้นอยู่กับบรรทัดใหม่แรก (และไม่รวม) หรือจุดสิ้นสุดของสตริงใดก็ตามที่มาก่อนและตัดการผสมของช่องว่างและ\tอักขระต่อท้ายและอักขระ หากคุณต้องการรักษาหลายบรรทัด (และยังตัดการขึ้นบรรทัดใหม่และการขึ้นบรรทัดใหม่) ให้ใช้read -r -d '' var << eofแทน อย่างไรก็ตามโปรดทราบว่าหากการป้อนข้อมูลของคุณมีอยู่ข้อมูล\neofนั้นจะถูกตัดออกไปก่อนหน้านี้ (พื้นที่สีขาวรูปแบบอื่น ได้แก่\r, \fและ\v, จะไม่ถูกถอดออกแม้ว่าคุณจะเพิ่มใน $ IFS)

read -r var << eof
$var
eof


5

สิ่งนี้จะลบช่องว่างทั้งหมดออกจากสตริงของคุณ

 VAR2="${VAR2//[[:space:]]/}"

/แทนที่การเกิดขึ้นครั้งแรกและการ//เกิด whitespaces ทั้งหมดในสตริง คือช่องว่างสีขาวทั้งหมดถูกแทนที่ด้วย - ไม่มีอะไร


4

นี่เป็นวิธีที่ง่ายที่สุดที่ฉันเคยเห็น มันใช้ Bash เพียงไม่กี่บรรทัด regexp นั้นง่ายและตรงกับทุกรูปแบบของช่องว่าง:

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

นี่คือสคริปต์ตัวอย่างเพื่อทดสอบ:

test=$(echo -e "\n \t Spaces and tabs and newlines be gone! \t  \n ")

echo "Let's see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let's fix that..."

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

echo
echo "----------"
echo -e "Testing:${test}:Tested"  # "Testing:Spaces and tabs and newlines be gone!"
echo "----------"
echo
echo "Ah, much better."

1
ตัวอย่างที่ดีกว่าอย่างแน่นอน (เย่พระเจ้า!), เปลือกออกไป Python ยกเว้นฉันคิดว่ามันง่ายกว่าและทั่วไปกว่าในการจัดการสตริงที่มีช่องว่างเท่านั้นการแสดงออกที่เรียบง่ายเล็กน้อยน่าจะเป็น:^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
Ron Burk

4

Python มีฟังก์ชั่นstrip()ที่ทำงานเหมือนกับ PHP'strim()ดังนั้นเราสามารถทำ Python แบบอินไลน์เพียงเล็กน้อยเพื่อสร้างยูทิลิตี้ที่เข้าใจได้ง่ายสำหรับสิ่งนี้:

alias trim='python -c "import sys; sys.stdout.write(sys.stdin.read().strip())"'

สิ่งนี้จะตัดช่องว่างนำหน้าและต่อท้าย (รวมถึงบรรทัดใหม่)

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi

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

3
#!/bin/bash

function trim
{
    typeset trimVar
    eval trimVar="\${$1}"
    read trimVar << EOTtrim
    $trimVar
EOTtrim
    eval $1=\$trimVar
}

# Note that the parameter to the function is the NAME of the variable to trim, 
# not the variable contents.  However, the contents are trimmed.


# Example of use:
while read aLine
do
    trim aline
    echo "[${aline}]"
done < info.txt



# File info.txt contents:
# ------------------------------
# ok  hello there    $
#    another  line   here     $
#and yet another   $
#  only at the front$
#$



# Output:
#[ok  hello there]
#[another  line   here]
#[and yet another]
#[only at the front]
#[]

3

ฉันพบว่าฉันต้องการเพิ่มโค้ดบางอย่างจากsdiffเอาท์พุตยุ่ง ๆเพื่อทำความสะอาด:

sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt 
sed -n 1'p' c12diff.txt | sed 's/ *$//g' | tr -d '\n' | tr -d '\t'

สิ่งนี้จะลบช่องว่างต่อท้ายและอักขระที่มองไม่เห็นอื่น ๆ


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