Bash変数から空白を削除する方法は?


922

このコードを含むシェルスクリプトがあります。

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

ただし、条件付きコードは常に実行されます。 hg st少なくとも1つの改行文字が出力されるれます。

  • $varPHPのようtrim()に空白を取り除く簡単な方法はありますか?)ますか?

または

  • この問題に対処する標準的な方法はありますか?

sedまたはAWKを使用することもできますが、この問題にはよりエレガントな解決策があると思います。


3
関連して、整数のスペースをトリムして整数を取得したい場合は、$(($ var))でラップします。また、二重引用符の内側でそれを行うこともできます。これは、dateステートメントとファイル名を使用するときに重要になりました。
Volomike

「この問題に対処する標準的な方法はありますか?」はい、[の代わりに[[を使用してください。$ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly 2016

それが役立つ場合は、少なくとも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

バッククォートが最後の改行を飲み込むため、改行のため、条件は真ではありません。これは何も出力しませんがtest=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi、これは出力するtest=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fiので、最後には改行だけではありません。
Mecki 2017年

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

回答:


1022

先頭、末尾、中間の空白を含む変数を定義しましょう:

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

先頭と末尾の両方のスペースを削除する方法sed-sをチェーンします。

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 | trimcat somefile | trim
instanceof me

sed2つではなく1つの式のみを使用するソリューションを作成しましたsed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'。先頭と末尾の空白を削除し、途中の空白以外の文字の空白で区切られたシーケンスをキャプチャします。楽しい!
Victor Zamanian 2014

@VictorZamanian入力に空白のみが含まれている場合、ソリューションは機能しません。MattyVとinstanceof meによって提供された2パターンのsedソリューションは、空白のみの入力で正常に動作します。
Torben 14

@Torben Fairポイント。|複数の式ではなく1つの式として保持するために、単一の式をで条件付きにすることができると思います。
Victor Zamanian 2014

966

簡単な答えは:

echo "   lol  " | xargs

Xargsがトリミングを行います。これは1つのコマンド/プログラムで、パラメーターはなく、トリミングされた文字列を返します。

注:これはすべての内部スペースを削除するわけではないため"foo bar"、同じままです。なりません"foobar"。ただし、複数のスペースは1つのスペースに集約されるため、"foo bar"になり"foo bar"ます。さらに、行末文字は削除されません。


27
いいね。これは本当にうまくいきます。私はそれをパイプしxargs echoて、私がやっていることについて冗長にすることを決めましたが、xargs自体はデフォルトでエコーを使用します。
2013

24
巧妙なトリックですが、注意してください。1行の文字列に使用できますが、xargs設計により、複数行のパイプ処理されたコンテンツでのトリミングだけではありません。sedはあなたの友達です。
Jocelyn delalande 2013年

22
xargsの唯一の問題は、改行が導入されることですsed 's/ *$//'。改行をオフにしたい場合は、代替案としてお勧めします。あなたは見ることができxargs、このような改行を:echo -n "hey thiss " | xargs | hexdump あなたは気づくでしょう改行です。同じことを:で 見ると、改行はありません。0a73asedecho -n "hey thiss " | sed 's/ *$//' | hexdump0073

8
注意してください。これは、xargsへの文字列の間に余分なスペースが含まれていると、ハードブレークします。「これは一つの議論です」のように。xargsは4つに分かれます。
bos

64
これは悪いです。1.それは変わりますa<space><space>ba<space>b。2.さらに:それは変わりますa"b"c'd'eabcde。3.さらに:失敗するa"bなど
サーシャ

359

ワイルドカードと呼ばれる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準拠のシェルで動作することです。

参照


18
賢い!これは、組み込みのbash機能を使用するため、私のお気に入りのソリューションです。投稿ありがとうございます!@San、それは2つのネストされた文字列トリムです。たとえば、s=" 1 2 3 "; echo \""${s%1 2 3 }"\"最後からすべてをトリミングして、先頭を返し" "ます。subbing 1 2 3 with [![:space:]]*は、「最初のスペース以外の文字を見つけてから、それ以降すべてを上書きする」ように指示します。の%%代わりに%を使用すると、末尾からのトリム操作が貪欲になります。これは貪欲ではない開始からのトリムにネストされているため、実際に" "は開始からトリムします。次に、%、#、および*を末尾のスペースに置き換えます。バム!
マークG.

2
不要な副作用は見つかりませんでした。メインコードは他のPOSIX風のシェルでも動作します。ただし、Solaris 10では動作しません/bin/sh(でのみ機能します/usr/xpg4/bin/shが、これは通常のshスクリプトで使用されるものではありません)。
vinc17

9
sed、trなどを使用するよりもはるかに高速なソリューションであり、fork()を回避します。Cygwinでは、速度の違いは桁違いです。
Gene Pavlovsky

9
@San最初は正規表現だと思って困りました。ではない。むしろ、これは、パターンマッチング構文(あるgnu.org/software/bash/manual/html_node/Pattern-Matching.htmlwiki.bash-hackers.org/syntax/pattern除去(サブストリングで使用される)tldp.org/LDP/absを/html/string-manipulation.html)。したがって${var%%[![:space:]]*}、「varスペース以外の文字で始まる最長の部分文字列から削除する」と言います。つまり、先行スペースのみが残され、その後で削除され${var#..ます。次の行(末尾)は反対です。
Ohad Schneider 2017

8
これは圧倒的に理想的なソリューションです。1つまたは複数の外部プロセスをforkすること(例えば、awksedtrxargs)単に一つの文字列からトリム空白に根本的に狂っている-ほとんどのシェルは(bashを含む)場合、特にすでにアウト・オブ・ボックスの設備をいじるネイティブ文字列を提供しています。
セシルカレー

81

Bashにはパラメーター拡張と呼ばれる機能があり、これにより、いわゆるパターンに基づいて文字列を置換できます(パターンは正規表現に似ていますが、根本的な違いと制限があります)。[flusenceの元の行: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の中央のスペースで機能しますが、最後に固定しようとすると機能しません。
ポールトンブリン

これは役に立ちますか?マンページから:「$ {parameter / pattern / string} [...] patternが%で始まる場合、parameterの展開された値の最後で一致する必要があります。」

@Ant、それで、それらは本当に正規表現ではありませんが、何か似ていますか?
Paul Tomblin、

3
彼らは正規表現で、奇妙な方言です。

13
${var/ /}最初のスペース文字を削除します。${var// /}すべてのスペース文字を削除します。この構成だけでは、先頭と末尾の空白のみを削除する方法はありません。
Gilles 'SO-悪をやめなさい'

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

1つの先行スペースと1つの後続スペースを削除する

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 2017

42

グロビングに関するBashガイドのセクションから

パラメータ展開で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";

サブシェルで実行するように関数を変更する場合、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
見てきたとおり、trim()は先頭と末尾の空白のみを削除します。
GuruM 2012

mkelementはすでに注記しているように、$(trim $ string)ではなく、引用符で囲まれた文字列、つまり$(trim "$ string")として関数パラメーターを渡す必要があります。正しい使い方を示すようにコードを更新しました。ありがとう。
GuruM 2012

シェルオプションについて知っていることに感謝しますが、2つのパターン置換を行うよりも最終的な結果がエレガントだとは思いません
sehe

(最近の十分なバージョンのBashでは)、次のコマンドextglobを使用してオプションを復元するためのメカニズムを簡略化できることに注意してくださいshopt -p:単にlocal restore="$(shopt -p extglob)" ; shopt -s extglob関数の先頭とeval "$restore"末尾に書き込みます(ただし、grant、evalは悪...)。
Maëlan

素晴らしい解決策!一つの潜在的な改善:それはのように見える[[:space:]]、よく、とスペースを交換することができます${var##+( )}し、${var%%+( )}仕事だけでなく、彼らが読みやすくなります。
DKroot

40

あなたは簡単にトリムすることができますecho

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

これにより、隣接する複数のスペースが1つに折りたたまれます。
Evgeni Sergeev

7
fooワイルドカードが含まれているときに試しましたか?例:foo=" I * have a wild card"...サプライズ!さらに、これはいくつかの隣接するスペースを1つに折りたたみます。
gniourf_gniourf 14

5
これは、次の場合に優れたソリューションです。1.両端にスペースを入れない2.各単語の間にスペースを1つだけ入れたい3.ワイルドカードを使用せずに制御された入力を処理している。それは本質的に悪いフォーマットのリストを良いものに変えます。
musicin3d 2015年

ワイルドカード@gniourf_gniourf +1の良いリマインダー。それでも優れたソリューション、ヴァンプ。あなたにも+1。
Beco博士2016年

25

私はいつもsedでそれをやってきました

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

よりエレガントな解決策があれば、誰かが投稿してくれるといいですね。


の構文を説明できますsedか?
farid99 2015

2
正規表現は、すべての末尾の空白に一致し、何も置き換えません。
Paul Tomblin、2015

4
空白を導くことはどうですか?
Qian Chen

これにより、末尾の空白がすべて削除されますsed -e 's/\s*$//'。説明:「s」は検索、「\ s」はすべての空白、「*」はゼロまたは複数、「$」は行末まで、「//」はすべての一致を空の文字列に置き換えることを意味します。
クレイグ

's / * $ //'で、アスタリスクの前に1つのスペースではなく2つのスペースがあるのはなぜですか?それはタイプミスですか?
Brent212


24

Bashの拡張パターンマッチング機能を有効にすると(shopt -s extglob)、これを使用できます。

{trimmed##*( )}

先頭のスペースを任意の量削除します。


すごい!これが最も軽量でエレガントなソリューションだと思います。
dubiousjim 2011年

1
以下の@GuruMの投稿を参照してください。(a)すべての形式の空白を扱い、(b)末尾の空白も処理する、より一般的な解決策。
mklement0

@mkelement +1。コードスニペットを関数として書き換える手間がかかります。おかげで
グルム2012

OpenBSDのデフォルトの/ bin / kshでも動作します。/bin/sh -o posixも動作しますが、私は不審です。
クリントパクル

ここではbashウィザードではありません。なに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で変数にその値のトリミングされたバージョン$ {!1}を格納します
Aquarius Power

2
trim()関数のパラメーターは変数名です。test_trim()内のtrim()の呼び出しを参照してください。test_trim()から呼び出されるtrim()内では、$ 1はfooに展開され、$ {!1}は$ foo(つまり、変数fooの現在の内容)に展開されます。bashマニュアルで「変数の間接参照」を検索してください。
flabdablet

1
1回の呼び出しで複数の変数のトリミングをサポートするために、この小さな変更はどうですか?trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
Gene Pavlovsky

2
@AquariusPowerワンライナーバージョンのサブシェルでエコーを使用する必要read -rd '' str <<<"$str"はありません。
flabdablet 2016

12

たくさんの答えがありますが、私が書いたばかりのスクリプトは言及する価値があると私はまだ信じています:

  • シェルbash / dash / busyboxシェルで正常にテストされました
  • とても小さい
  • 外部コマンドに依存せず、フォークする必要もありません(->高速でリソース使用量が少ない)
  • 期待どおりに動作します:
    • それはストリップのすべての先頭と末尾から空白とタブをではなく、より多くの
    • 重要:文字列の途中から何も削除されません(他の多くの回答では削除されます)。改行も残ります。
    • 特殊:"$*"1つのスペースを使用して複数の引数を結合します。最初の引数のみをトリムして出力する場合は、"$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 in Cの方が実装が簡単です。
Nils

承知しました。残念ながら、これはCではなく、外部ツールの呼び出しを避けたい場合があります
Daniel Alder

両方の読みやすく、コピー・過去の互換性のあるコードを作成するには、エスケープ文字にブラケットを変更することができます:[\ \t]
leondepeon

@leondepeonやってみましたか?書いたときに試してみましたが、あなたの提案がbash、dash、busyboxのいずれでも機能しません
Daniel Alder

@DanielAlderを使用しましたが、すでに3年前なので、使用したコードが見つかりません。しかし今、私はおそらく[[:space:]]他の答えの1つで同様に使用します:stackoverflow.com/a/3352015/3968618
leondepeon

11

オールドスクールを使用できますtr。たとえば、これはgitリポジトリ内の変更されたファイルの数を返し、空白は削除されます。

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

1
これは前後の空白を削除しません-文字列からすべての空白を削除します。
Nick

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
私のために働いていませんでした。最初のものはトリミングされていない文字列を出力しました。2番目は悪い置換を投げました。ここで何が起こっているのか説明できますか?
musicin3d 2015年

1
@ musicin3d:これは私が頻繁に使用するサイトで、bash検索で変数操作がどのように機能するか${var##Pattern}を詳しく説明しています。また、このサイトではbashパターンについて説明しています。つまり、##指定されたパターンを前面から%%削除することと、指定されたパターンを背面から削除することを意味します。+( )部分パターンであり、それは「一つまたは空間をより発生」を意味する
gMale

面白いことに、それはプロンプトで機能しましたが、bashスクリプトファイルに置き換えた後では機能しませんでした。
Beco博士2016年

変。両方のインスタンスで同じbashバージョンですか?
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")"
}

または

moskitのexprソリューションに基づいて構築しています...

# 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
変数の割り当てではなく、空白の合体を行うのは、引用符で囲ま$xyzechoていないwithの使用です。例の変数にトリミングされた値を格納するには、を使用する必要があります。また、このアプローチは、望ましくない可能性のあるパス名展開(グロビング)の影響を受けます。xyz=$(echo -n $xyz)
mklement0

これはまったく間違っていますxyz。変数の値はトリミングされません。
シーザーソル2015年

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

1
2つの単語の間に複数のスペースがある場合、これは機能しません。試してください:echo $(echo "1 2 3")(1、2、3の間に2つのスペースを入れて)。
joshlf 2013

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)関数trim内で使用される複数行の文字列に対する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 2012

1
あなたは間違っています:複数行の文字列でも動作します。試してみてください!:)
Luca Borrione

使用方法の+1-コードを簡単にテストできるようになりました。ただし、コードは複数行の文字列に対しては機能しません。出力を注意深く見ると、先頭/末尾の内部スペースもすべて削除されていることに気づくでしょう。たとえば、「複数行」の前のスペースは「複数行」に置き換えられます。各行の先頭/末尾のスペースの数を増やしてみてください。
GuruM 2012

今、私はあなたが何を意味するのかわかります!頭を上げてくれてありがとう、私は私の答えを編集しました。
Luca Borrione、2012

@ "Luca Borrione"-ようこそ:-) trim()で使用しているsed構文を説明できますか?また、コードのすべてのユーザーが他の用途にコードを微調整するのにも役立ちます。また、正規表現のエッジケースを見つけるのにも役立ちます。
GuruM 2012

6

空白を削除して正規化するtrim()関数を次に示します

#!/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
2番目の正規表現ベースのアプローチは素晴らしく、副作用はありませんが、現在の形式では問題があります。 (b)正規表現自体は、入力文字列がスペースで囲まれた単一の非スペース文字である場合を処理しません。これらの問題を修正するには、次のif行に置き換えますif [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]。最後に、このアプローチはスペースのみを扱い、他の形式の空白は扱いません(私の次のコメントを参照)。
mklement0

2
正規表現を使用する関数はスペースのみを扱い、他の形式の空白は扱いませんが、一般化するのは簡単です:次のif行に置き換えます:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0

6

AWKを使用:

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

機能しているように見える甘いもの(ex :) $stripped_version=echo $ var | awk '{gsub(/ ^ + | + $ /、 "")} 1
rogerdpack

4
awkが何もしていないことを除いて:引用符で囲まれていない変数をエコーすると、すでに空白が取り除かれています
glenn jackman

6

割り当ては、先頭と末尾の空白を無視するため、トリムに使用できます。

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

8
それは真実ではない。割り当てではなく、空白を削除するのは「エコー」です。あなたの例ではecho "$var"、スペースを含む値を確認してください。
Nicholas Sushkin、2012

2
@NicholasSushkin 1つは可能です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:]]/}"

///文字列内の最初のオカレンスとすべてのオカレンスを置き換えます。つまり、すべての空白は置き換えられます–何も


4

これは私が見た中で最も簡単な方法です。これはBashを使用するだけで、数行だけです。正規表現は単純で、空白のすべての形式に一致します。

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に砲撃するよりも確かに好ましいです。:私はそれが正しくのみspaces.Slightly簡素化の式は次のようになり含む文字列処理するために、よりシンプルで一般的だと思う除き^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
ロン・バーク

4

Pythonには、strip()PHPと同じように機能する関数があります。trim()ため、少しインライン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

それが機能している間は、文字列をトリミングするためだけに完全なPythonインタープリターを起動する必要のないソリューションを提供することを検討してください。それはもったいない。
pdwalker 2015

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.