Boşluk Bash değişkeninden nasıl kesilir?


920

Bu kod ile bir kabuk komut dosyası var:

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

Ancak koşullu kod her zaman yürütülür, çünkü hg sther zaman en az bir yeni satır karakteri yazdırır.

  • Dan boşluk şerit basit bir yol var mı $var(gibi trim()içinde PHP )?

veya

  • Bu sorunla başa çıkmanın standart bir yolu var mı?

Sed veya AWK kullanabilirdim , ancak bu soruna daha zarif bir çözüm olduğunu düşünmek istiyorum.


3
İlgili olarak, bir tamsayıdaki alanı kırpmak ve sadece tamsayıyı almak istiyorsanız, $ (($ var)) ile sarın ve bunu çift tırnak içinde bile yapabilirsiniz. Tarih ifadesini kullandığımda ve dosya adlarıyla bu önemli hale geldi.
Volomike

"Bu sorunla baş etmenin standart bir yolu var mı?" Evet, [yerine [[kullanın. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly

Eğer yardımcı olursa, en azından Ubuntu 16.04'te test ediyorum. Aşağıdaki maçları her şekilde kırpmak kullanma: echo " This is a string of char " | xargs. Eğer Ancak metinde tek tırnağı varsa aşağıdakileri yapabilirsiniz: echo " This i's a string of char " | xargs -0. En son xargs (4.6.0)
Luis Alvarado'dan

Durum, yeni satırsonu nedeniyle geri dönmez, son satırsonu yutar. Bu hiçbir şey basmayacak test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi, ancak bu olacak test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi- bu yüzden sonunda sadece bir satırsonu olmak zorunda.
Mecki

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

Yanıtlar:


1021

Öncü, sondaki ve ara boşluk içeren bir değişken tanımlayalım:

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

Nasıl (ile gösterilir tüm boşlukları silebilirsiniz [:space:]içinde 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

Yalnızca önde gelen boşluk nasıl kaldırılır:

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

Yalnızca sondaki boşluk nasıl kaldırılır:

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

Hem önde gelen hem de arkadaki boşluklar nasıl kaldırılır - zincirleri 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

Senin bash destekliyorsa Alternatif olarak, yerine echo -e "${FOO}" | sed ...ile sed ... <<<${FOO}şöyle (sonlarındaki boşluk için),:

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

63
Tüm boşluk biçimlerini işleyecek çözümü genelleştirmek için , trve sedkomutlarındaki boşluk karakterini değiştirin [[:space:]]. sedYaklaşımın yalnızca tek satır girişlerde işe yarayacağını unutmayın . Çok satırlı girişle çalışan ve aynı zamanda bash'ın yerleşik özelliklerini kullanan yaklaşımlar için @bashfu ve @GuruM tarafından verilen yanıtlara bakın. @Nicholas Sushkin'in çözümünün genelleştirilmiş, satır içi sürümü şu şekilde görünecektir: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0

7
Sık sık bunu yaparsak, ekleme alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"adresinden Müşteri ~/.profilekullanmak için izin verir echo $SOMEVAR | trimve cat somefile | trim.
instanceof me

sedİki değil, tek bir ifade kullanan bir çözüm yazdım sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. Öndeki ve sondaki boşlukları düzeltir ve ortadaki boşluk olmayan karakterlerin boşluklarla ayrılmış dizilerini yakalar. Zevk almak!
Victor Zamanian

@VictorZamanian Giriş yalnızca boşluk içeriyorsa çözümünüz çalışmaz. MattyV ve instanceof tarafından verilen iki model sed çözümleri yalnızca boşluk girdisi ile iyi çalışıyor.
Torben

@Torben Fuar noktası. Sanırım |tek bir ifade, birkaç değil tek bir ifade olarak tutmak için koşullu yapılabilir .
Victor Zamanian

966

Basit bir cevap:

echo "   lol  " | xargs

Xargs sizin için düzeltme yapacak. Bu bir komut / program, parametre yok, kesilmiş dizeyi döndürür, bu kadar kolay!

Not: Bu, tüm dahili alanları kaldırmaz, bu nedenle "foo bar"aynı kalır; bu OLMAYACAK "foobar". Bununla birlikte, birden çok boşluk tek boşluklara yoğunlaştırılacak, böylece "foo bar"olacak "foo bar". Ayrıca satır sonu karakterlerini kaldırmaz.


27
Güzel. Bu gerçekten iyi çalışıyor. Ben xargs echosadece ne yaptığım hakkında ayrıntılı olmak için boru karar verdim , ama kendi başına xargs varsayılan olarak yankı kullanır.
Will

24
Güzel hüner, ama dikkatli olun, tek satırlık dize için kullanabilirsiniz, ancak "xargs tasarımı" sadece çok satırlı boru içeriği ile kırpma yapmaz. sed o zaman senin arkadaşın.
Jocelyn delalande

22
Xargs ile tek sorun, bir satırsonu tutmak istiyorum, eğer satırsonu kapalı tutmak istiyorsanız sed 's/ *$//'alternatif olarak tavsiye ederim . Görebilirsiniz xargsBöyle bir yeni satır: echo -n "hey thiss " | xargs | hexdump Eğer göreceksiniz satır olduğunu. Eğer aynı yaparsanız : göreceksiniz , hiçbir yeni satır. 0a73asedecho -n "hey thiss " | sed 's/ *$//' | hexdump0073

8
Dikkatli; xargs dizesi arasında fazla boşluklar varsa bu zorlaşır. "Bu bir argüman" gibi. xargs dörde bölünecektir.
bos

64
Bu kötü. 1. a<space><space>bdönüşecek a<space>b. 2. Daha da fazlası: a"b"c'd'edönüşecek abcde. 3. Dahası: başarısız olacak a"b, vs.
Sasha

357

Sadece joker olarak adlandırılan Bash yerleşiklerini kullanan bir çözüm var :

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

İşte bir fonksiyonda sarılı olan:

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

Kesilecek dizeyi tırnak biçiminde iletirsiniz. Örneğin:

trim "   abc   "

Bu çözümle ilgili güzel bir şey, herhangi bir POSIX uyumlu kabukla çalışmasıdır.

Referans


17
Zeki! Yerleşik bash işlevini kullandığı için bu benim en sevdiğim çözüm. Gönderdiğiniz için teşekkürler! @San, iç içe geçmiş iki dize trimi. Örneğin, s=" 1 2 3 "; echo \""${s%1 2 3 }"\"her şeyi baştan sona düzeltir, önderlik eder " ". Subbing 1 2 3 ile [![:space:]]*"ilk boşluk olmayan karakteri bul, sonra onu ve sonrasındaki her şeyi bul" yazar. %%Bunun yerine kullanmak %uçtan uca işlemi açgözlü yapar. Bu, başlangıçtan itibaren açgözlü olmayan bir trim içine yerleştirilmiştir, bu yüzden aslında, " "baştan başlatırsınız. Ardından, son boşluklar için%, # ve * işaretlerini değiştirin. Bam!
Mark G.12

2
İstenmeyen yan etkiler bulamadım ve ana kod diğer POSIX benzeri kabuklarla da çalışıyor. Bununla birlikte, Solaris 10 altında, ile çalışmaz /bin/sh(sadece /usr/xpg4/bin/sh, ancak normal sh komut dosyalarıyla kullanılacak olan şey bu değildir).
vinc17

9
Sed, tr vb kullanmaktan çok daha iyi bir çözümdür, çünkü çok daha hızlıdır, herhangi bir çataldan kaçınır (). Cygwin üzerinde hız farkı büyüklük sıralamasıdır.
Gene Pavlovsky

9
@San İlk başta güvendim çünkü bunların düzenli ifadeler olduğunu düşündüm. Onlar değil. Aksine, bu Substring Removal'da ( tldp.org/LDP/abs ) kullanılan Kalıp Eşleştirme sözdizimidir ( gnu.org/software/bash/manual/html_node/Pattern-Matching.html , wiki.bash-hackers.org/syntax/pattern ). /html/string-manipulation.html ). Yani ${var%%[![:space:]]*}" varboşluk olmayan bir karakterle başlayan en uzun alt dizesinden kaldır" diyor . Bu, yalnızca sonradan kaldıracağınız ön boşluklarla kaldığınız anlamına gelir ${var#... Aşağıdaki satır (sondaki) tam tersidir.
Ohad Schneider

8
Bu ezici bir çoğunlukla ideal çözümdür. Bir veya daha fazla dış işlemleri çatallamak (ör awk, sed, tr, xargs), sadece tek bir dizgeden Döşeme boşluk temelde insane - özellikle en kabukların (dahil darbe) zaten dışı-box tesislerini munging yerli dize sağlamaktadır.
Cecil Curry

81

Bash, parametre genişletme adı verilen bir özelliğe sahiptir, bu özellik , diğer şeylerin yanı sıra, sözde desenlere dayalı dizginin değiştirilmesine izin verir (desenler düzenli ifadelere benzer, ancak temel farklılıklar ve sınırlamalar vardır). [flussence'ın orijinal satırı: Bash'in düzenli ifadeleri var, ancak iyi gizlenmişler:]

Aşağıda, tüm beyaz alanların (iç kısımdan bile) değişken bir değerden nasıl kaldırılacağı gösterilmektedir .

$ 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
Daha doğrusu, bir varlığın ortasındaki boşluklar için çalışır, ancak sonunda tutturmaya çalıştığımda değil.
Paul Tomblin

Bu yardımcı olur mu? "$ {Parametre / desen / string} [...] Desen% ile başlıyorsa, parametrenin genişletilmiş değerinin sonunda eşleşmesi gerekir."

@Ant, yani gerçekten düzenli ifadeler değil, benzer bir şey mi?
Paul Tomblin

3
Normal ifade ediyorlar, sadece garip bir lehçe.

13
${var/ /}ilk boşluk karakterini kaldırır. ${var// /}tüm boşluk karakterlerini kaldırır. Sadece bu yapı ile sadece öndeki ve sondaki boşlukları kırpmanın bir yolu yoktur.
Gilles 'SO- kötü olmayı kes'

60

Bir dizenin başından ve sonundan tüm satırları kaldırmak için (satır sonu karakterleri dahil):

echo $variable | xargs echo -n

Bu, yinelenen boşlukları da kaldıracaktır:

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

Üretiyor: 'bu dizgede çok fazla alan var'


5
Temel olarak xargs tüm sınırlayıcıları dizeden kaldırır. Varsayılan olarak, alanı ayırıcı olarak kullanır (bu, -d seçeneğiyle değiştirilebilir).
rkachach

4
Bu, en temiz (hem kısa hem de okunabilir) çözümdür.
Potherca

Neden hiç ihtiyacın var echo -n? echo " my string " | xargsaynı çıktıya sahiptir.
bfontaine

echo -n satır sonunu da kaldırır
rkachach

55

Bir öncü ve bir arka boşluk bırakın

trim()
{
    local trimmed="$1"

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

    echo "$trimmed"
}

Örneğin:

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

Çıktı:

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

Tüm önde gelen ve arkadaki alanları soyun

trim()
{
    local trimmed="$1"

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

    echo "$trimmed"
}

Örneğin:

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

Çıktı:

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

9
Bu sadece 1 boşluk karakteri keser. Böylece yankı sonuç verir'hello world ', 'foo bar', 'both sides '
Joe

@Joe Daha iyi bir seçenek ekledim.
wjandrea

42

Globbing ile ilgili Bash Guide bölümünden

Parametre genişletmesinde bir extglob kullanmak için

 #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  

İşte bir işlevin içine sarılmış aynı işlevsellik (NOT: İşleve geçirilen girdi dizesini alıntılamak gerekir):

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.
}

Kullanımı:

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

Bir alt kabukta yürütülecek işlevi değiştirirsek, extglob için geçerli kabuk seçeneğini inceleme konusunda endişelenmemize gerek kalmaz, sadece geçerli kabuğu etkilemeden ayarlayabiliriz. Bu, işlevi muazzam bir şekilde basitleştirir. Ayrıca "yerinde" konum parametrelerini de güncelliyorum, bu yüzden yerel bir değişkene bile ihtiyacım yok

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

yani:

$ 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
gördüğünüz gibi trim () yalnızca önde gelen ve arkadaki boşlukları kaldırır.
GuruM

Mkelement'in daha önce de belirttiği gibi, fonksiyon parametresini $ (trim $ string) yerine $ (trim "$ string") olarak belirtilen bir dize olarak geçirmeniz gerekir. Doğru kullanımı göstermek için kodu güncelledim. Teşekkürler.
GuruM

Kabuk seçeneklerini bilmeyi takdir ettiğim kadarıyla, son sonucun sadece 2 desen yerine
koyma

(Bash'in yeterince yeni bir sürümüyle) seçeneği extglobkullanarak seçeneği geri yükleme mekanizmasını basitleştirebileceğinizi unutmayın shopt -p: sadece local restore="$(shopt -p extglob)" ; shopt -s extglobfonksiyonunuzun başlangıcına ve eval "$restore"sonunda yazın (verilenler hariç, eval kötüdür).
Maëlan

Harika bir çözüm! Potansiyel bir gelişme: Görünüşe göre [[:space:]]bir boşlukla değiştirilebilir: ${var##+( )}ve ${var%%+( )}aynı zamanda işe yarar ve okunması daha kolaydır.
DKroot

40

Basitçe aşağıdakilerle kırpabilirsiniz echo:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

Bu, birden fazla bitişik alanı bir taneye daraltır.
Evgeni Sergeev

7
Zaman bunu denedin mi foobir joker içeriyor? örneğin, foo=" I * have a wild card"... sürpriz! Dahası, bu birkaç bitişik alanı tek bir alana daraltır.
gniourf_gniourf

5
Bu, eğer mükemmel bir çözümdür: 1. uçlarda boşluk istemez 2. her kelime arasında sadece bir boşluk ister 3. joker karakterleri olmayan kontrollü bir girişle çalışıyoruz. Aslında kötü biçimlendirilmiş bir listeyi iyi bir listeye dönüştürür.
musicin3d

Joker karakterlerin iyi hatırlatıcısı @gniourf_gniourf +1. Hala mükemmel bir çözüm, Vamp. Size de + 1'leyin.
Dr Beco

25

Her zaman sed ile yaptım

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

Daha zarif bir çözüm varsa, umarım birisi gönderir.


sözdizimini açıklayabilir misiniz sed?
farid99

2
Normal ifade, izleyen tüm boşluklarla eşleşir ve onu hiçbir şeyle değiştirmez.
Paul Tomblin

4
Lider boşluklara ne dersiniz?
Qian Chen

Bu, tüm boşlukları kapatır sed -e 's/\s*$//'. Açıklama: 's' arama, '\ s' tüm boşluk anlamına gelir, '*' sıfır veya çok anlamına gelir, '$' satır sonuna kadar ve '//' tüm eşleşmeleri boş bir dizeyle değiştirir .
Craig

'S / * $ //' de, yıldız işaretinden önce neden tek bir boşluk yerine 2 boşluk var? Bu bir yazım hatası mı?
Brent212

24

Yeni satırları aşağıdakilerle silebilirsiniz tr:

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done

8
'\ N' dizesinin ortasından kaldırmak istemiyorum, sadece başlangıçtan veya sondan.
Çok fazla php

24

Bash'ın genişletilmiş desen eşleme özellikleri etkinken ( shopt -s extglob) şunları kullanabilirsiniz:

{trimmed##*( )}

gelişigüzel miktarda önde gelen boşlukları kaldırmak için.


Müthiş! Bence bu en hafif ve zarif çözüm.
dubiousjim

1
Benzer bir için aşağıdaki GuruM yayınının @ Bak, ama (a) fırsatlar olduğunu daha genel bir çözüm tüm boşluk ve (b) biçimleri de kolları sondaki boşluk.
mklement0

@mkelement +1, kod snippet'imi bir işlev olarak yeniden yazmakta sorun yaşadığınız için. Teşekkürler
GuruM

OpenBSD'nin varsayılan / bin / ksh ile de çalışır. /bin/sh -o posixben de çalışıyor ama şüpheliyim.
Clint Pachl

Burada bir bash sihirbazı değil; nedir trimmed? Yerleşik bir şey mi yoksa kesilen değişken mi?
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
İnanılmaz! Basit ve etkili! Açıkçası benim en sevdiğim çözüm. Teşekkür ederim!
xebeche

1
@CraigMcQueen değişken olarak read1 $ adıyla değişkente $ {! 1} değerinin kesilmiş bir versiyonunda saklanacağı gibi
Aquarius Power

2
Trim () işlevinin parametresi değişken bir addır: test_trim () içindeki trim () çağrısına bakın. Test_trim () öğesinden çağrılan trim () içinde $ 1, foo'ya ve $ {! 1}, $ foo'ya (yani, değişken foo'nun geçerli içeriğine) genişler. Bash kılavuzunda 'değişken dolaylı' arayın.
flabdablet

1
Bir çağrıda çoklu değişkenleri kırpmayı desteklemek için bu küçük modifikasyona ne dersiniz? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
Gene Pavlovsky

2
@AquariusPower, tek katmanlı sürüm için bir alt kabukta yankı kullanmaya gerek yok, sadece read -rd '' str <<<"$str"yapacak.
flabdablet

12

Çok fazla cevap var, ama hala yazdığım senaryodan bahsetmeye değer olduğuna inanıyorum çünkü:

  • başarıyla bash / dash / busybox kabuğunda test edildi
  • son derece küçük
  • harici komutlara bağlı değildir ve çatallanmaya gerek yoktur (-> hızlı ve düşük kaynak kullanımı)
  • beklendiği gibi çalışır:
    • o şeritler tüm başı ve sonu boşlukları ve sekmeleri değil, daha
    • önemli: dizenin ortasından hiçbir şey kaldırmaz (diğer birçok yanıt yapar), hatta yeni satırlar kalır
    • special: "$*"bir boşluk kullanarak birden çok argümanı birleştirir. yalnızca ilk bağımsız değişkeni kırpmak ve çıktısını almak istiyorsanız "$1"bunun yerine
    • dosya adı desenlerini eşleştirme konusunda herhangi bir sorun yoksa vb.

Senaryo:

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"
}

Kullanımı:

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

Çıktı:

>here     is
    something<

C Bah bu uygulamak çok daha kolay olurdu!
Nils

Elbette. Ne yazık ki, bu C değil ve bazen harici araçları çağırmaktan kaçınmak istersiniz
Daniel Alder

Kodu hem daha okunabilir hem de kopya geçmişiyle uyumlu hale getirmek için, köşeli parantezleri kaçan karakterlerle değiştirebilirsiniz:[\ \t]
leondepeon

@leondepeon bunu denedin mi? Yazdığımda denedim ve tekrar denedim ve öneriniz bash, dash, busybox'ın hiçbirinde çalışmıyor
Daniel Alder

@DanielAlder yaptım, ama zaten 3 yıl önce olduğu gibi, kullandığım kodu bulamıyorum. Ancak şimdi, muhtemelen [[:space:]]diğer cevaplardan birinde olduğu gibi kullanacağım : stackoverflow.com/a/3352015/3968618
leondepeon

11

Eski okulu kullanabilirsiniz tr. Örneğin, bir boşluk deposundaki değiştirilmiş dosyaların sayısını döndürür.

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

1
Bu, boşlukları önden ve arkadan kesmez - tüm boşlukları dizeden kaldırır.
Nick

11

Bu benim için çalıştı:

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>

Aynı sonuç için daha az satıra koymak için:

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

1
Benim için çalışmadı. Birincisi, eşi görülmemiş bir dize yazdırdı. İkincisi kötü oyuncu değişikliği attı. Burada neler olduğunu açıklayabilir misiniz?
musicin3d

1
@ musicin3d: Bu, daha fazla ayrıntı için bash aramasında değişken manipülasyonun nasıl çalıştığını açıklayan sık kullandığım bir sitedir ${var##Pattern}. Ayrıca, bu site bash kalıplarını açıklar . Böylece ##araçlar verilen deseni önden %%kaldırır ve verilen deseni arkadan kaldırır. Bölüm +( )
örüntüdür ve

Komik, komut isteminde çalıştı, ancak bash komut dosyasına aktarıldıktan sonra değil.
Dr Beco

tuhaf. Her iki durumda da aynı bash versiyonu mu?
2016

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

VEYA

# 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")")"
}

VEYA

# 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]}"
}

VEYA

# 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")"
}

VEYA

Moskit'in ifade ruhu üzerine inşa ...

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

VEYA

# 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

Ben komut dosyaları sadece iş yapmak için değişken atama kullanın gördüm:

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

Boşluk otomatik olarak birleştirilir ve kırpılır. Kabuk metakarakterlerine dikkat edilmelidir (potansiyel enjeksiyon riski).

Ayrıca, kabuk koşullarında her zaman çift tırnaklı değişken ikamelerini tavsiye ederim:

if [ -n "$var" ]; then

çünkü değişkente -o veya başka bir içerik test argümanlarınızı değiştirebilir.


3
Bu bir tırnaksız kullanımıdır $xyzile echobu boşluk coalescing, does not değişken atama. Kesilen değeri örneğinizdeki değişkende saklamak için kullanmanız gerekir xyz=$(echo -n $xyz). Ayrıca, bu yaklaşım potansiyel olarak istenmeyen yol adı genişletmesine (globbing) tabidir.
mklement0

bu yanlıştır, xyzdeğişken içindeki değer kırpılmaz.
caesarsol

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

1
İki kelime arasında birden fazla boşluk varsa bu işe yaramaz. Deneyin: echo $(echo "1 2 3")(1, 2 ve 3 arasında iki boşluk olacak şekilde).
joshlf

7

Ben sadece sed kullanırdım:

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

a) Tek satırlı dize kullanım örneği

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

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

Çıktı:

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

b) Çok satırlı dizede kullanım örneği

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

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

Çıktı:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) Son not:
Bir işlev kullanmak istemiyorsanız, tek satırlık dize için aşağıdaki gibi "hatırlaması daha kolay" komutunu kullanabilirsiniz:

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

Misal:

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

Çıktı:

wordA wordB wordC

Yukarıdakileri çok satırlı dizelerde kullanmak da işe yarayacaktır , ancak GuruM'nin yorumlarda fark ettiği gibi, arkadaki / önde gelen dahili çoklu alanı da keseceğini lütfen unutmayın.

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

Çıktı:

wordAA
>four spaces before<
>one space before<

Bu boşlukları saklamayı düşünürseniz, lütfen cevabımın başındaki fonksiyonu kullanın!

d) sed sözdiziminin fonksiyon triminde kullanılan çok satırlı dizelerde "bul ve değiştir" açıklaması:

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
}'

Not: @mkelement tarafından önerildiği gibi, tek satırlı dizeler için çalışması gerekir, ancak çok satırlı dize için çalışmaz.
GuruM

1
Yanılıyorsunuz: Çok satırlı dizelerde de çalışıyor. Sadece test edin! :)
Luca Borrione

+1 kullanımı için - kodu test etmemi kolaylaştırdı. Ancak kod yine de çok satırlı dizelerde çalışmaz. Çıktıya dikkatlice bakarsanız, önde gelen / arkadaki iç boşlukların da kaldırıldığını göreceksiniz, örneğin "çok satırlı" öğenin önündeki alan "çok satırlı" ile değiştirilir. Her satırda önde gelen / arkadaki boşluk sayısını artırmayı deneyin.
GuruM

Şimdi ne demek istediğini anlıyorum! Başınız için teşekkür ederim, cevabımı düzenledim.
Luca Borrione

@ "Luca Borrione" - hoşgeldiniz :-) trim () içinde kullandığınız sed sözdizimini açıklar mısınız? Ayrıca, kodunuzdaki herhangi bir kullanıcının kodu diğer kullanımlar için değiştirmesine yardımcı olabilir. Ayrıca, düzenli ifade için uç durumlar bulmaya bile yardımcı olabilir.
GuruM

6

Boşluğu kesen ve normalleştiren bir trim () işlevi

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

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

Ve düzenli ifadeler kullanan başka bir varyant.

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

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

İlk yaklaşım, yalnızca iç beyazlığı normalleştirmekle kalmaz (beyaz alanın tüm iç açıklıklarını her biri tek bir boşlukla değiştirir), aynı zamanda globbing'e (yol adı genişletme) tabidir, örneğin, *giriş dizesindeki bir karakter geçerli çalışma klasöründeki tüm dosya ve klasörlere genişletilir. Son olarak, $ IFS varsayılan olmayan bir değere ayarlanırsa, kırpma çalışmayabilir (ancak bu ekleyerek kolayca düzeltilebilir local IFS=$' \t\n'). Kırpma şu boşluk formlarıyla sınırlıdır: boşluklar \tve \nkarakterler.
mklement0

1
İkinci, düzenli ifade tabanlı yaklaşım harika ve yan etkiye sahip değildir, ancak mevcut formunda problemlidir: (a) bash v3.2 + 'da, eşleme varsayılan olarak ÇALIŞMAZ, çünkü normal ifade sırayla Uncommoted edilmelidir ve (b) normal ifadenin kendisi, girdi dizesinin boşluklarla çevrili tek, boşluk olmayan bir karakter olduğu durumu ele almaz. Bu sorunları gidermek için, değiştirmek ifsatırı: if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]. Son olarak, yaklaşım diğer boşluk formlarıyla değil, yalnızca boşluklarla ilgilidir (bir sonraki yorumuma bakın).
mklement0

2
Düzenli expresssions kullanır işlevi yalnızca ilgilenir boşluklar ve boşluk değil başka biçimlerde, ama genelleme kolaydır: Değiştir ifsatırı:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0

6

AWK kullan:

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

Çalıştığı gibi tatlı (ex :) $stripped_version=echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1''`
rogerdpack

4
awk dışında hiçbir şey yapmaz: alıntılanmamış bir değişkeni yankılamak zaten boşluktan çıkardı
Glenn Jackman

6

Atamalar, öndeki ve sondaki boşlukları yok sayar ve bu nedenle kırpmak için kullanılabilir:

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

8
Bu doğru değil. Boşluğu kaldıran "eko", ödevi değil. Örneğinizde, echo "$var"boşluklu değeri görmek için yapın.
Nicholas Sushkin

2
@NicholasSushkin Biri yapabilirdi var=$(echo $var)ama tavsiye etmiyorum. Burada sunulan diğer çözümler tercih edilir.
xebeche

5

Bu, istenmeyen globbing ile ilgili bir sorun oluşturmaz, ayrıca, iç beyaz boşluk değiştirilmez (varsayılan $IFSolarak ayarlandığı varsayılarak,' \t\n' ).

Hangisi önce gelirse, ilk satırsonuna (ve onu içermez) veya dizenin sonuna kadar okur ve önde gelen ve sondaki boşluk ve \tkarakterlerin karışımını kaldırır . Birden fazla satırı korumak istiyorsanız (ve ayrıca satır başı ve son satır satırlarını sıyırmak istiyorsanız) read -r -d '' var << eofbunun yerine; Ancak, girdiniz içeriyorsa \neof, daha önce kesileceğini unutmayın. (Yani boşluk, diğer şekilleri \r, \fve \v, olan değil sen $ IFS eklemek bile soyulmuş.)

read -r var << eof
$var
eof


5

Bu, tüm beyaz alanları Dizenizden kaldıracak,

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

///dizede ilk ve beyaz boşlukların tümünün yerini alır . Yani tüm beyaz alanların yerini alıyor - hiçbir şey


4

Bu gördüğüm en basit yöntem. Sadece Bash kullanır, sadece birkaç satırdır, normal ifade basittir ve tüm boşluk alanlarıyla eşleşir:

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

İşte sınamak için örnek bir komut dosyası:

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
Elbette, örneğin (siz tanrılar!), Python'a bombardıman etmek için tercih edilir. Dışında sadece boşluk içeren dize doğru işlemek için daha basit ve daha genel olduğunu düşünüyorum. Biraz basitleştirilmiş ifade şöyle olurdu:^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
Ron Burk

4

Python, strip()PHP'lerle aynı şekilde çalışan bir işleve sahiptir trim(), bu yüzden bunun için kolayca anlaşılabilir bir yardımcı program yapmak için biraz satır içi Python yapabiliriz:

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

Bu, önde gelen ve sondaki boşlukları (yeni satırlar dahil) keser.

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

bu çalışırken, yalnızca bir dizeyi kırpmak için tam bir python yorumlayıcısı başlatmayı içermeyen bir çözüm sunmayı düşünebilirsiniz. Sadece israf.
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

sdiffTemizlemek için dağınık bir çıktı bazı kod eklemek için gerekli bulundu :

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'

Bu, sondaki boşlukları ve diğer görünmez karakterleri kaldırır.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.