Как обрезать пробелы из переменной Bash? - PullRequest
781 голосов
/ 16 декабря 2008

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

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

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

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

или

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

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

Ответы [ 41 ]

20 голосов
/ 16 декабря 2008

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

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done
17 голосов
/ 25 января 2012
# 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
11 голосов
/ 21 октября 2015

Есть много ответов, но я все еще считаю, что мой только что написанный сценарий стоит упомянуть, потому что:

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

Сценарий:

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<
11 голосов
/ 27 ноября 2009

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

MYVAR=`git ls-files -m|wc -l|tr -d ' '`
9 голосов
/ 01 мая 2017
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

OR

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

OR

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

OR

Опираясь на опыт Москита ...

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

OR

# 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")")"
}
9 голосов
/ 11 сентября 2014

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

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##+( )}%%+( )}
8 голосов
/ 16 декабря 2008

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

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

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

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

if [ -n "$var" ]; then

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

7 голосов
/ 01 апреля 2013
var='   a b c   '
trimmed=$(echo $var)
7 голосов
/ 27 августа 2012

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

б) Пример использования многострочной строки

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 "найти и заменить" для многострочных строк, используемых внутри функции trim:

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
}'
6 голосов
/ 01 октября 2010

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

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