Как обрезать пробелы из переменной Bash?

У меня есть оболочка script с этим кодом:

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

Но условный код всегда выполняется, потому что hg st всегда печатает хотя бы один символ новой строки.

  • Есть ли простой способ удалить пробелы из $var (например, trim() в PHP)?

или

  • Существует ли стандартный способ решения этой проблемы?

Я мог бы использовать sed или AWK, но я бы хотел подумать, что есть более элегантное решение этой проблемы.

Ответ 1

Извините, проблема в другом месте в моем script, и я думал, что var имеет в ней конечную новую строку, но на самом деле это не так. Командная строка замещения завершает новые строки автоматически, как указано здесь: http://tldp.org/LDP/abs/html/commandsub.html.

Ответ 2

Определить переменную, содержащую начальные, конечные и промежуточные пробелы:

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

Ответ 3

Простой ответ:

echo "   lol  " | xargs

Xargs выполнит обрезку. Это одна команда/программа, без параметров, возвращает обрезанную строку, просто, как это!

Примечание: это не устраняет внутренние пространства, поэтому "foo bar" остается неизменным. Он не становится "foobar".

Ответ 4

Существует решение, в котором используются только Bash встроенные подстановочные знаки:

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

Здесь же завернутый в функцию:

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   
    echo -n "$var"
}

Вы передаете строку, подлежащую обрезке в кавычках. например:

trim "   abc   "

Хорошая вещь об этом решении заключается в том, что он будет работать с любой POSIX-совместимой оболочкой.

Ссылка

Ответ 5

Bash имеет функцию, называемую расширением параметра, которая, среди прочего, позволяет замену строк на основе так называемых шаблонов (шаблоны напоминают регулярные выражения, но там являются фундаментальными различиями и ограничениями). [flussence original line: Bash имеет регулярные выражения, но они хорошо скрыты:]

Ниже показано, как удалить все белое пространство (даже изнутри) из значения переменной.

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

Ответ 6

Удалите один ведущий и один завершающий пробел

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'

Ответ 7

Чтобы удалить все пробелы с начала и конца строки (включая символы конца строки):

echo $variable | xargs echo -n

Это также удалит повторяющиеся пробелы:

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

Производит: "эта строка имеет много пробелов"

Ответ 8

Вы можете обрезать только с помощью echo:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

Ответ 9

Из раздела Bash Guide по глобализации

Использовать 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

Ответ 10

С включенными функциями совместимости с расширенными шаблонами Bash (shopt -s extglob) вы можете использовать это:

{trimmed##*( )}

чтобы удалить произвольное количество начальных пробелов.

Ответ 11

Я всегда делал это с sed

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

Если есть более элегантное решение, я надеюсь, что кто-то разместит его.

Ответ 12

Вы можете удалить символы новой строки с помощью tr:

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

Ответ 13

# 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

Ответ 14

Вы можете использовать старую школу tr. Например, это возвращает количество измененных файлов в репозитории git, удаленные пробелы.

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

Ответ 15

Есть много ответов, но я по-прежнему считаю, что мой просто написанный script стоит упомянуть, потому что:

  • он был успешно протестирован в оболочках bash/dash/busybox shell
  • он чрезвычайно мал
  • он не зависит от внешних команд и не нужен fork (- > быстрый и низкий ресурс)
  • работает как и ожидалось:
    • он удаляет все пробелы и вкладки с начала и конца, но не более
    • important: он не удаляет ничего из середины строки (многие другие ответы), даже новые строки будут оставаться
    • special: "$*" объединяет несколько аргументов, используя одно пространство. если вы хотите обрезать и выводить только первый аргумент, используйте "$1" вместо
    • Если у вас нет проблем с совпадением шаблонов имен файлов и т.д.

script:

trim() {
  local s2 s="$*"
  # note: the brackets in each of the following two lines contain one space
  # and one tab
  until s2="${s#[   ]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[   ]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

Использование:

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

Вывод:

>here     is
    something<

Ответ 16

# 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 soulution...

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

Ответ 17

Это сработало для меня:

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##+( )}%%+( )}

Ответ 18

Я видел, что сценарии используют назначение переменной для выполнения задания:

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

Пробел автоматически объединяется и обрезается. Нужно быть осторожным с метасимволами оболочки (потенциальный риск инъекций).

Я бы также рекомендовал всегда заменять двойные кавычки на переменные оболочки:

if [ -n "$var" ]; then

поскольку что-то вроде -o или другого содержимого в переменной может изменить ваши тестовые аргументы.

Ответ 19

Я бы просто использовал 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
}'

Ответ 20

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

Ответ 21

Используйте AWK:

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

Ответ 22

Присвоения игнорируют начальные и конечные пробелы и как таковые могут использоваться для обрезки:

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

Ответ 23

Здесь функция trim(), которая обрезает и нормализует пробелы

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

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

И еще один вариант, который использует регулярные выражения.

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

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

Ответ 25

У этого нет проблемы с нежелательным globbing, также внутреннее белое пространство немодифицировано (предполагается, что для параметра $IFS установлено значение по умолчанию, которое равно ' \t\n').

Он считывает первую строку новой строки (и не включает ее) или конец строки, в зависимости от того, что наступит раньше, и удаляет любое сочетание верхнего и конечного пробелов и символов \t. Если вы хотите сохранить несколько строк (а также разделите ведущую и конечную строки новой строки), используйте read -r -d '' var << eof; обратите внимание, однако, что если ваш вход будет содержать \neof, он будет отключен раньше. (Другие формы белого пространства, а именно \r, \f и \v, не удаляются, даже если вы добавляете их в $IFS.)

read -r var << eof
$var
eof

Ответ 26

Это удалит все пробелы из вашей строки,

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

/ заменяет первое вхождение и // все вхождения пробелов в строке. То есть все белые пробелы заменяются на - ничего

Ответ 27

Это самый простой метод, который я видел. Он использует только Bash, это всего несколько строк, регулярное выражение простое и соответствует всем формам пробелов:

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

Вот пример script, чтобы проверить его с помощью

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

echo "Let see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let 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."

Ответ 28

Я создал следующие функции. Я не уверен, насколько переносимым является printf, но красота этого решения заключается в том, что вы можете точно указать, что такое "пробел", добавляя больше кодов символов.

    iswhitespace()
    {
        n=`printf "%d\n" "'$1'"`
        if (( $n != "13" )) && (( $n != "10" )) && (( $n != "32" )) && (( $n != "92" )) && (( $n != "110" )) && (( $n != "114" )); then
            return 0
        fi
        return 1
    }

    trim()
    {
        i=0
        str="$1"
        while (( i < ${#1} ))
        do
            char=${1:$i:1}
            iswhitespace "$char"
            if [ "$?" -eq "0" ]; then
                str="${str:$i}"
                i=${#1}
            fi
            (( i += 1 ))
        done
        i=${#str}
        while (( i > "0" ))
        do
            (( i -= 1 ))
            char=${str:$i:1}
            iswhitespace "$char"
            if [ "$?" -eq "0" ]; then
                (( i += 1 ))
                str="${str:0:$i}"
                i=0
            fi
        done
        echo "$str"
    }

#Call it like so
mystring=`trim "$mystring"`

Ответ 29

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

Ответ 30

Я обнаружил, что мне нужно добавить код из беспорядочного вывода 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'

Это удаляет конечные пробелы и другие невидимые символы.