วิธีการตัดช่องว่างจากตัวแปร 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.