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

701

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

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

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

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

или

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

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

  • 3
    Связанный, если вы хотите обрезать пространство на целое число и просто получить целое число, оберните с $ (($ var)), и даже можете сделать это, когда внутри двойных кавычек. Это стало важным, когда я использовал оператор даты и с именами файлов.
  • 0
    «Есть ли стандартный способ решения этой проблемы?» Да, используйте [[вместо [. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
Показать ещё 3 комментария
Теги:
string
variables
trim

42 ответа

10
Лучший ответ

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

812

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

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})"
  • 57
    Чтобы обобщить решение для обработки всех форм пробелов, замените символ пробела в командах tr и sed на [[:space:]] . Обратите внимание, что подход sed будет работать только на однострочном вводе. Подходы, которые работают с многострочным вводом, а также используют встроенные функции bash, см. В ответах @bashfu и @GuruM. Обобщенная, встроенная версия решения @Nicholas Sushkin будет выглядеть следующим образом: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
  • 7
    Если вы делаете это часто, добавьте alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'" в ~/.profile позволяет использовать echo $SOMEVAR | trim и cat somefile | trim
Показать ещё 8 комментариев
671

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

echo "   lol  " | xargs

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

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

  • 22
    Ницца. Это работает очень хорошо. Я решил xargs echo его в xargs echo только для того, чтобы подробно рассказать о том, что я делаю, но xargs сам по себе будет использовать эхо по умолчанию.
  • 0
    На Windows это очень хорошее решение. Я обычно использую двоичные файлы gnu utils без bash (например, cygwin, unxutils, пользовательские сборки, mingw, git для windows, gnuwin32 ...), и поддержка sed и awk не очень согласована между ними.
Показать ещё 13 комментариев
264

Существует решение, в котором используются только 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-совместимой оболочкой.

Ссылка

  • 0
    @luca borrione: local var = "$ 1"
  • 0
    Я попробовал ваше решение, но использование local var = "$ @" сжимает несколько внутренних пробелов в один пробел . Таким образом, строка с несколькими пробелами, например, «a <multi-space> bcd», будет переводиться в «abc d» с одним пробелом. Единственный способ, предложенный @mkelement, - это заключить в кавычки непосредственно входную переменную, то есть обрезать «$ string», в этом случае $ @ в любом случае расширяется до одного параметра.
Показать ещё 6 комментариев
65

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
  • 1
    Кажется, он не работает с Cygwin.
  • 2
    Или, скорее, это работает для пробелов в середине var, но не когда я пытаюсь закрепить его в конце.
Показать ещё 9 комментариев
36

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

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'
  • 0
    Это объединяет несколько смежных пространств в одно.
  • 5
    Вы пробовали, когда foo содержит подстановочный знак? например, foo=" I * have a wild card" ... сюрприз! Более того, это объединяет несколько смежных пространств в одно.
Показать ещё 2 комментария
36

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

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
    Это обрезает только 1 пробел. Таким образом, эхо приводит к 'hello world ', 'foo bar', 'both sides '
  • 0
    @ Джо, я добавил лучший вариант.
31

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

echo $variable | xargs echo -n

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

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

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

  • 0
    Как это работает?
  • 4
    По сути, xargs удаляет все разделители из строки. По умолчанию он использует пробел в качестве разделителя (это можно изменить с помощью параметра -d).
Показать ещё 3 комментария
26

Из раздела 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
  • 2
    как вы заметили, trim () удаляет только начальные и конечные пробелы.
  • 0
    Как уже заметил mkelement, вам нужно передать параметр функции в виде строки в кавычках, т.е. $ (trim "$ string") вместо $ (trim $ string). Я обновил код, чтобы показать правильное использование. Благодарю.
Показать ещё 4 комментария
22

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

{trimmed##*( )}

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

  • 0
    Потрясающе! Я думаю, что это самое легкое и элегантное решение.
  • 1
    Смотрите пост @ GuruM ниже для аналогичного, но более общего решения, которое (а) имеет дело со всеми формами пустого пространства и (б) также обрабатывает конечные пробелы.
Показать ещё 4 комментария
18

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

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done
  • 6
    Я не хочу удалять '\ n' из середины строки, только из начала или конца.
17
# 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
  • 1
    Удивительно! Просто и эффективно! Понятно мое любимое решение. Спасибо!
  • 1
    Что делает ${!1} ? Это не совсем понятно.
Показать ещё 5 комментариев
16

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

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

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

  • 0
    Не могли бы вы объяснить синтаксис для sed ?
  • 2
    Регулярное выражение соответствует всем конечным пробелам и заменяет его ничем.
Показать ещё 2 комментария
10

Есть много ответов, но я по-прежнему считаю, что мой просто написанный 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<
  • 0
    Бах в С это было бы проще реализовать!
  • 1
    хорошо, может быть, не проще, но гораздо меньше "WTF ?!"
Показать ещё 3 комментария
10

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

MYVAR=`git ls-files -m|wc -l|tr -d ' '`
  • 0
    Это не удаляет пробелы спереди и сзади - оно удаляет все пробелы из строки.
8

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

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
    Не работал для меня Первый напечатал не обрезанную строку. Второй бросил плохую замену. Можете ли вы объяснить, что здесь происходит?
  • 1
    @ musicin3d: это сайт, которым я часто пользуюсь, который объясняет, как работает манипулирование переменными в поиске bash для ${var##Pattern} для получения более подробной информации. Также этот сайт объясняет паттерны bash . Таким образом, ## означает удаление данного шаблона спереди, а %% означает удаление данного шаблона сзади. Часть +( ) является шаблоном и означает «одно или несколько вхождений пробела»
Показать ещё 2 комментария
7
var='   a b c   '
trimmed=$(echo $var)
  • 0
    Это не сработает, если между любыми двумя словами будет больше одного пробела. Попробуйте: echo $(echo "1 2 3") (с двумя пробелами между 1, 2 и 3).
7

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

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

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

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

if [ -n "$var" ]; then

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

  • 3
    Использование без кавычек $xyz с echo делает объединение пробелов, а не присваивание переменных. Чтобы сохранить усеченное значение в переменной в вашем примере, вы должны будете использовать xyz=$(echo -n $xyz) . Кроме того, этот подход подвержен потенциально нежелательному расширению пути (globbing).
  • 0
    это просто неправильно, значение в переменной xyz НЕ обрезается.
6
# 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")")"
}
6

Я бы просто использовал 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
}'
  • 0
    Примечание: как предложено @mkelement, он не будет работать для многострочных строк, хотя он должен работать для однострочных строк.
  • 1
    Вы не правы: он работает и на многострочных строках. Просто проверьте это! :)
Показать ещё 5 комментариев
5

Здесь функция 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'
  • 0
    Первый подход сложен в том, что он не только нормализует внутренние пробелы (заменяет все внутренние промежутки пробелами на один пробел каждый), но также подвергается глобализации (расширение пути), так что, например, символ * во входной строке расширится на все файлы и папки в текущей рабочей папке. Наконец, если для $ IFS установлено значение не по умолчанию, обрезка может не работать (хотя это легко исправить, добавив local IFS=$' \t\n' ). Обрезка ограничена следующими формами пробелов: пробелы, символы \t и \n .
  • 1
    Второй подход, основанный на регулярных выражениях, хорош и лишен побочных эффектов, но в его нынешнем виде проблематичен: (a) в bash v3.2 + сопоставление по умолчанию НЕ будет работать, потому что регулярное выражение должно быть в кавычках, чтобы работать и (б) само регулярное выражение не обрабатывает случай, когда входная строка представляет собой один непробельный символ, окруженный пробелами. Чтобы устранить эти проблемы, замените строку if на: if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]] . Наконец, подход касается только пробелов, а не других форм пробелов (см. Мой следующий комментарий).
Показать ещё 1 комментарий
4

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

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

read -r var << eof
$var
eof
4

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

$ var=`echo '   hello'`; echo $var
hello
  • 7
    Это не правда. Это «эхо», которое удаляет пробелы, а не назначение. В вашем примере выполните echo "$var" чтобы увидеть значение с пробелами.
  • 2
    @NicholasSushkin Можно было бы сделать var=$(echo $var) но я не рекомендую это делать. Другие решения, представленные здесь, являются предпочтительными.
4

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

echo $var | awk '{gsub(/^ +| +$/,"")}1'
  • 0
    Сладкий, который, кажется, работает (ex :) $stripped_version= echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1'``
  • 4
    за исключением того, что awk ничего не делает: вывод переменной без кавычек уже удаляет пробелы
3

Это самый простой метод, который я видел. Он использует только 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."
  • 1
    Конечно, предпочтительнее, например (вы, боги!), Обстреливать Python. За исключением того, что я думаю, что проще и более общим правильно обрабатывать строку, содержащую только пробелы. Слегка упрощенным выражением будет: ^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
3

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

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

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

3

Чтобы удалить пробелы и вкладки слева направо, введите:

echo "     This is a test" | sed "s/^[ \t]*//"

cyberciti.biz/tips/delete-leading-spaces-from-front-of-each-word.html

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]
#[]
2

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

trim() {
    local orig="$1"
    local trmd=""
    while true;
    do
        trmd="${orig#[[:space:]]}"
        trmd="${trmd%[[:space:]]}"
        test "$trmd" = "$orig" && break
        orig="$trmd"
    done
    printf -- '%s\n' "$trmd"
}
  • Он работает со всеми типами пробелов, включая новую строку,
  • Не нужно изменять shopt.
  • Он сохраняет внутри пробелов, включая новую строку.

Unit test (для ручного просмотра):

#!/bin/bash

. trim.sh

enum() {
    echo "   a b c"
    echo "a b c   "
    echo "  a b c "
    echo " a b c  "
    echo " a  b c  "
    echo " a  b  c  "
    echo " a      b  c  "
    echo "     a      b  c  "
    echo "     a  b  c  "
    echo " a  b  c      "
    echo " a  b  c      "
    echo " a N b  c  "
    echo "N a N b  c  "
    echo " Na  b  c  "
    echo " a  b  c N "
    echo " a  b  c  N"
}

xcheck() {
    local testln result
    while IFS='' read testln;
    do
        testln=$(tr N '\n' <<<"$testln")
        echo ": ~~~~~~~~~~~~~~~~~~~~~~~~~ :" >&2
        result="$(trim "$testln")"
        echo "testln='$testln'" >&2
        echo "result='$result'" >&2
    done
}

enum | xcheck
2

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
  • 0
    в то время как это работает, вы можете подумать о предложении решения, которое не предполагает запуска полного интерпретатора Python просто для обрезки строки. Это просто расточительно.
2

trim() удаляет пробелы (и вкладки, непечатаемые символы, для простоты я рассматриваю просто пробелы). Моя версия решения:

var="$(hg st -R "$path")" # I often like to enclose shell output in double quotes
var="$(echo "${var}" | sed "s/\(^ *\| *\$\)//g")" # This is my suggestion
if [ -n "$var" ]; then
 echo "[${var}]"
fi

Команда "sed" обрезает только ведущие и завершающие пробелы, но ее можно передать в первую команду, а также:

var="$(hg st -R "$path" | sed "s/\(^ *\| *\$\)//g")"
if [ -n "$var" ]; then
 echo "[${var}]"
fi
2

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

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

2

Я создал следующие функции. Я не уверен, насколько переносимым является 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"`
2

Это уменьшает количество пробелов переднего и конечного

whatever=${whatever%% *}

whatever=${whatever#* }

  • 7
    Ваша вторая команда должна иметь ## не только # . Но на самом деле они не работают; шаблон, который вы даете, соответствует пробелу, за которым следует любая последовательность других символов, а не последовательность из 0 или более пробелов. Это * оболочка * , а не обычное регулярное выражение "0 или более" * .
  • 1
    Это полезно, если вы знаете, что в строке не будет пробелов. Они отлично работают для преобразования " foo " в "foo" . Для " hello world " , не так много.
Показать ещё 1 комментарий
1

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

var=`expr "$var" : "^\ *\(.*[^ ]\)\ *$"`

Он удаляет ведущие и конечные пробелы и является самым основным решением, я считаю. Не Bash встроенный, но 'expr' является частью coreutils, поэтому, по крайней мере, не требуются автономные утилиты, такие как sed или AWK.

1
var="  a b  "
echo "$(set -f; echo $var)"

>a b
1

Используйте это простое расширение Bash:

$ x=" a z     e r ty "
$ echo "START[${x// /}]END"
START[azerty]END
  • 1
    Этот подход (а) также удаляет внутренние пробелы и (б) удаляет только пробелы , а не другие формы пустого пространства.
  • 0
    @ mklement0 Иногда это именно то, что нужно, хотя! :-)
Показать ещё 1 комментарий
1

Еще одно решение с модульными тестами, которое обрезает $IFS от stdin и работает с любым разделителем ввода (даже $'\0'):

ltrim()
{
    # Left-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local trimmed
    while IFS= read -r -d "${1-}" -u 9
    do
        if [ -n "${trimmed+defined}" ]
        then
            printf %s "$REPLY"
        else
            printf %s "${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
        printf "${1-\x00}"
        trimmed=true
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        if [ -n "${trimmed+defined}" ]
        then
            printf %s "$REPLY"
        else
            printf %s "${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
    fi
}

rtrim()
{
    # Right-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local previous last
    while IFS= read -r -d "${1-}" -u 9
    do
        if [ -n "${previous+defined}" ]
        then
            printf %s "$previous"
            printf "${1-\x00}"
        fi
        previous="$REPLY"
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        last="$REPLY"
        printf %s "$previous"
        if [ -n "${previous+defined}" ]
        then
            printf "${1-\x00}"
        fi
    else
        last="$previous"
    fi

    right_whitespace="${last##*[!$IFS]}"
    printf %s "${last%$right_whitespace}"
}

trim()
{
    # Trim $IFS from individual lines
    # $1: Line separator (default NUL)
    ltrim ${1+"$@"} | rtrim ${1+"$@"}
}
1

Мне нужно было обрезать пробелы из script, когда переменная IFS была установлена ​​на что-то еще. Опираясь на Perl сделал трюк:

# trim() { echo $1; } # This doesn't seem to work, as it affected by IFS

trim() { echo "$1" | perl -p -e 's/^\s+|\s+$//g'; }

strings="after --> , <-- before,  <-- both -->  "

OLD_IFS=$IFS
IFS=","
for str in ${strings}; do
  str=$(trim "${str}")
  echo "str= '${str}'"
done
IFS=$OLD_IFS
  • 1
    Вы можете легко избежать проблем со значениями $ IFS, отличными от заданных по умолчанию, создав локальную копию (которая выйдет из области видимости после выхода из функции): trim() { local IFS=$' \t\n'; echo $1; }
1

Удаление пробелов в одно пространство:

(text) | fmt -su
-3
#Execute this script with the string argument passed in double quotes !! 
#var2 gives the string without spaces.
#$1 is the string passed in double quotes
#!/bin/bash
var2=`echo $1 | sed 's/ \+//g'`
echo $var2
-4

Пока это не строго Bash, это сделает то, что вы хотите, и многое другое:

php -r '$x = trim("  hi there  "); echo $x;'

Если вы хотите сделать это в нижнем регистре, выполните следующие действия:

php -r '$x = trim("  Hi There  "); $x = strtolower($x) ; echo $x;'

Ещё вопросы

Сообщество Overcoder
Наверх
Меню