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

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

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

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

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

или

  • Есть ли стандартный способ решения этой проблемы?

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

15.12.2008 21:24:01
Связанный, если вы хотите обрезать пространство на целое число и просто получить целое число, оберните с $ (($ var)), и даже можете сделать это, когда внутри двойных кавычек. Это стало важным, когда я использовал оператор даты и с именами файлов.
Volomike 10.12.2012 14:42:55
«Есть ли стандартный способ решения этой проблемы?» Да, используйте [[вместо [. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly 26.04.2016 16:08:01
Если это поможет, по крайней мере, где я тестирую это на 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 19.07.2016 16:48:19
Условие не выполняется из-за новой строки, поскольку обратные метки поглощают последнюю новую строку. Это ничего не будет печатать test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi, однако это будет test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi- поэтому в конце должно быть больше, чем просто перевод строки.
Mecki 19.06.2017 17:02:16
A = "123 4 5 6"; B = echo $A | sed -r 's/( )+//g';
bruziuz 17.07.2018 11:25:34
30 ОТВЕТОВ

Я всегда делал это с помощью sed

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

Если есть более элегантное решение, надеюсь, кто-нибудь опубликует его.

24
15.12.2008 21:28:48
Не могли бы вы объяснить синтаксис для sed?
farid99 8.07.2015 20:21:21
Регулярное выражение соответствует всем конечным пробелам и заменяет его ничем.
Paul Tomblin 8.07.2015 20:23:20
Как насчет ведущих пробелов?
Qian Chen 10.02.2016 06:28:28
Это удаляет все конечные пробелы sed -e 's/\s*$//'. Объяснение: «s» означает поиск, «\ s» означает все пробелы, «*» означает ноль или многие, «$» означает до конца строки, а «//» означает замену всех совпадений пустой строкой ,
Craig 12.04.2016 12:21:31
В 's / * $ //', почему перед звездочкой стоит 2 пробела, а не один пробел? Это опечатка?
Brent212 19.03.2020 18:01:37

В Bash есть функция, называемая расширением параметров , которая, помимо прочего, позволяет заменять строки на основе так называемых шаблонов (шаблоны напоминают регулярные выражения, но есть фундаментальные различия и ограничения). [Оригинальная строка flussence: у 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
79
4.06.2012 15:38:58
Вернее, он работает для пробелов в середине переменной, но не тогда, когда я пытаюсь закрепить его в конце.
Paul Tomblin 15.12.2008 21:56:49
Это помогает любому? Из man-страницы: «$ {параметр / шаблон / строка} [...] Если шаблон начинается с%, он должен совпадать в конце расширенного значения параметра.»
user42092 15.12.2008 22:28:33
@ Неужели они не регулярные выражения, а что-то похожее?
Paul Tomblin 15.12.2008 22:40:56
Они регулярные выражения, просто странный диалект.
user42092 5.03.2009 12:36:09
${var/ /}удаляет первый пробел ${var// /}удаляет все пробелы Невозможно обрезать только начальные и конечные пробелы только с помощью этой конструкции.
Gilles 'SO- stop being evil' 3.06.2012 19:47:54

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

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done
23
15.12.2008 21:42:26
Я не хочу удалять '\ n' из середины строки, только из начала или конца.
too much php 15.12.2008 21:48:16

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

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

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

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

if [ -n "$var" ]; then

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

8
15.12.2008 21:57:01
Это неупомянуто использование $xyzс , echoчто делает пробельную коалесцирующее, не переменная назначению. Чтобы сохранить усеченное значение в переменной в вашем примере, вы должны будете использовать xyz=$(echo -n $xyz). Кроме того, этот подход подвержен потенциально нежелательному расширению пути (globbing).
mklement0 4.06.2012 04:20:45
это просто неправильно, значение в xyzпеременной НЕ обрезается.
caesarsol 10.06.2015 14:24:00

Уберите один ведущий и один пробел

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'
55
9.12.2017 02:08:28
Это обрезает только 1 пробел. Таким образом, эхо приводит к'hello world ', 'foo bar', 'both sides '
Joe 14.07.2014 15:33:52
@ Джо, я добавил лучший вариант.
wjandrea 9.12.2017 02:09:41

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

echo $var | awk '{gsub(/^ +| +$/,"")}1'
6
25.09.2015 18:45:24
Сладкий, который, кажется, работает (ex :) $stripped_version=echo $ var | awk '{gsub (/ ^ + | + $ /, "")} 1'``
rogerdpack 15.04.2010 18:29:44
кроме того, что awk ничего не делает: эхо-переменная без
glenn jackman 8.06.2011 10:41:31

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

MYVAR=`git ls-files -m|wc -l|tr -d ' '`
11
27.11.2009 09:49:36
Это не удаляет пробелы спереди и сзади - оно удаляет все пробелы из строки.
Nick 6.12.2013 15:28:36

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

{trimmed##*( )}

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

24
25.09.2015 18:46:08
Потрясающе! Я думаю, что это самое легкое и элегантное решение.
dubiousjim 27.01.2011 18:50:56
Смотрите пост @ GuruM ниже для аналогичного, но более общего решения, которое (а) имеет дело со всеми формами пустого пространства и (б) также обрабатывает конечные пробелы.
mklement0 3.06.2012 19:55:12
@mkelement +1 за труд переписать мой фрагмент кода как функцию. Спасибо
GuruM 21.09.2012 05:20:15
Также работает с OpenBSD по умолчанию / bin / ksh. /bin/sh -o posixтоже работает но я с подозрением
Clint Pachl 16.03.2018 19:19:25
Здесь не волшебник Баш; что trimmed? Это встроенная вещь или переменная, которая обрезается?
Abhijit Sarkar 30.03.2018 17:51:21

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

(text) | fmt -su
3
25.09.2015 18:46:28

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

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:

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

С другой стороны , если ваш Баш поддерживает его, вы можете заменить echo -e "${FOO}" | sed ...с sed ... <<<${FOO}, например , так (для конечных пробелов):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"
1013
20.06.2017 10:14:06
Обобщая решение для обработки всех форм пробелов, заменить символ пробела в trи sedкоманде с [[:space:]]. Обратите внимание, что этот sedподход будет работать только на однострочном вводе. Подходы, которые работают с многострочным вводом, а также используют встроенные функции bash, см. В ответах @bashfu и @GuruM. Обобщенная, встроенная версия решения @Nicholas Sushkin будет выглядеть следующим образом: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0 10.06.2012 14:05:37
Если вы делаете это часто, добавление alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"к вашему ~/.profileпозволяет вам использовать echo $SOMEVAR | trimи cat somefile | trim.
instanceof me 19.03.2013 15:59:52
Я написал sedрешение , которое использует только одно выражение , а не два: sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. Он обрезает начальные и конечные пробелы и фиксирует любые разделенные пробелами последовательности непробельных символов в середине. Наслаждайтесь!
Victor Zamanian 6.02.2014 17:41:01
@VictorZamanian Ваше решение не работает, если входные данные содержат только пробелы. Решения sed с двумя шаблонами, предоставленные MattyV и instanceof me, прекрасно работают с вводом только пробелов.
Torben 9.04.2014 07:38:44
@ Торбен Справедливая точка. Я полагаю, что одно выражение можно сделать условным, |чтобы сохранить его как одно, а не несколько выражений.
Victor Zamanian 9.04.2014 13:59:21

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

Ссылка

349
5.03.2020 04:46:25
Умная! Это мое любимое решение, так как оно использует встроенную функциональность bash. Спасибо за публикацию! @ Сан, это две вложенные строки. Например, s=" 1 2 3 "; echo \""${s%1 2 3 }"\"урезать все с конца, возвращая ведущий " ". Подбирая 1 2 3 с [![:space:]]*указывает, что «найти первый непробельный символ, затем забить его и все после». Использование %%вместо %делает операцию отделки с конца жадной. Это вложено в не жадную обрезку с самого начала, так что, по сути, вы обрезаете " "с самого начала. Затем поменяйте местами%, # и * для конечных пробелов. Бам!
Mark G. 12.11.2014 17:04:23
Я не нашел никаких нежелательных побочных эффектов, и основной код работает и с другими POSIX-подобными оболочками. Однако в Solaris 10 он не работает /bin/sh(только с /usr/xpg4/bin/sh, но это не то, что будет использоваться с обычными sh-скриптами).
vinc17 26.04.2016 11:57:51
Намного лучшее решение, чем использование sed, tr и т. Д., Так как это намного быстрее, избегая использования fork (). На Cygwin разница в скорости составляет порядки величины.
Gene Pavlovsky 3.07.2016 09:33:34
@San Сначала я был в тупике, потому что думал, что это были регулярные выражения. Они не. Скорее всего , это шаблон синтаксис ( gnu.org/software/bash/manual/html_node/Pattern-Matching.html , wiki.bash-hackers.org/syntax/pattern ) , используемый в Substring удаления ( tldp.org/LDP/abs /html/string-manipulation.html ). Так ${var%%[![:space:]]*}говорит «удалить из varсамой длинной подстроки, которая начинается с непробельного символа». Это означает, что у вас остались только первые пробелы, которые вы впоследствии удалите ${var#... Следующая строка (трейлинг) противоположна.
Ohad Schneider 25.01.2017 18:50:45
В подавляющем большинстве это идеальное решение. Ветвление один или несколько внешних процессов (например, awk, sed, tr, xargs) просто обрезки пробельного из одной строки в корне безумен - особенно , когда большинство оболочек ( в том числе Баша) уже обеспечивает встроенную строку munging объектов вне коробки.
Cecil Curry 14.08.2018 20:44:55

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

$ var=`echo '   hello'`; echo $var
hello
6
25.09.2015 18:47:01
Это не правда. Это «эхо», которое удаляет пробелы, а не назначение. В вашем примере сделайте, echo "$var"чтобы увидеть значение с пробелами.
Nicholas Sushkin 9.02.2012 17:41:08
@NicholasSushkin Можно было бы сделать, var=$(echo $var)но я не рекомендую это. Другие решения, представленные здесь, являются предпочтительными.
xebeche 2.08.2012 11:08:53

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

whatever=${whatever%% *}

whatever=${whatever#* }

2
11.10.2010 13:17:37
Ваша вторая команда, вероятно, должна иметь ##не только #. Но на самом деле это не работает; шаблон, который вы даете, соответствует пробелу, за которым следует любая последовательность других символов, а не последовательность из 0 или более пробелов. Это *оболочка *, а не обычное регулярное выражение "0 или более" *.
dubiousjim 27.01.2011 18:50:14
Это полезно, если вы знаете, что в строке не будет пробелов. Они прекрасно работают для преобразования " foo "в "foo". Ибо " hello world "не так много.
J.D. 28.02.2015 16:29:15
t=" "" a "" "; echo "=${t#* }="выходные = a =ведущие несколько пробелов не удаляются. t=" "" a "" "; echo "=${t%% *}="выходные ==концевые пробелы очищаются от содержимого строки. (Двойная кавычка из-за SE.)
catpnosis 24.03.2018 16:08:49

Я создал следующие функции. Я не уверен, насколько переносим 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"`
3
25.09.2015 18:48:33

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

# trim() { echo $1; } # This doesn't seem to work, as it's 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
2
25.09.2015 18:49:20
Вы можете легко избежать проблем с нестандартными значениями $ IFS, создав локальную копию (которая выйдет из области действия после выхода из функции):trim() { local IFS=$' \t\n'; echo $1; }
mklement0 2.06.2012 03:17:26

Вот функция 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'
6
8.06.2011 16:43:16
Первый подход сложен в том, что он не только нормализует внутренние пробелы (заменяет все внутренние промежутки пробелами на один пробел каждый), но также подвергается смещению (расширению пути), так что, например, *символ во входной строке будет разверните все файлы и папки в текущей рабочей папке. Наконец, если для $ IFS задано значение не по умолчанию, обрезка может не работать (хотя это легко исправить, добавив local IFS=$' \t\n'). Обрезка ограничена следующими формами пробелов: пробелы \tи \nсимволы.
mklement0 4.06.2012 21:53:20
Второй подход, основанный на регулярных выражениях, великолепен и не имеет побочных эффектов, но в его нынешнем виде проблематичен: (a) в bash v3.2 + сопоставление по умолчанию НЕ будет работать, поскольку регулярное выражение должно быть в кавычках, чтобы работать и (б) само регулярное выражение не обрабатывает случай, когда входная строка представляет собой один непробельный символ, окруженный пробелами. Чтобы устранить эти проблемы, замените ifстроку с: if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]. Наконец, подход касается только пробелов, а не других форм пробелов (см. Мой следующий комментарий).
mklement0 4.06.2012 21:53:47
Функция, которая использует регулярные выражения, имеет дело только с пробелами, а не с другими формами пробелов, но ее легко обобщить: замените ifстроку на:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0 4.06.2012 21:54:23

Из раздела 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
41
13.12.2018 15:45:11
как вы заметили, trim () удаляет только начальные и конечные пробелы.
GuruM 12.09.2012 13:03:07
Как уже заметил mkelement, вам нужно передать параметр функции в виде строки в кавычках, т.е. $ (trim "$ string") вместо $ (trim $ string). Я обновил код, чтобы показать правильное использование. Спасибо.
GuruM 18.09.2012 09:57:18
Как бы мне не хотелось знать о параметрах оболочки, я не думаю, что конечный результат более элегантен, чем
sehe 1.11.2016 12:33:21
Обратите внимание, что (с достаточно недавней версией Bash?) Вы можете упростить механизм восстановления опции extglob, используя shopt -p: просто написать local restore="$(shopt -p extglob)" ; shopt -s extglobв начале вашей функции и eval "$restore"в конце (за исключением того, что eval - это зло…).
Maëlan 24.03.2019 22:56:29

Еще одно решение с модульными тестами, которое обрезается $IFSот стандартного ввода и работает с любым входным разделителем (даже $'\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
21.09.2011 11:04:17

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

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

read -r var << eof
$var
eof
5
4.06.2012 04:52:35
#!/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
2.12.2011 02:26:32
# 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
19
25.01.2012 08:45:42
Удивительно! Просто и эффективно! Понятно мое любимое решение. Спасибо!
xebeche 2.08.2012 11:21:43
@CraigMcQueen это значение переменной, так как она readбудет хранить в переменной по ее имени $ 1 урезанную версию ее значения $ {! 1}
Aquarius Power 2.04.2015 20:54:24
Параметр функции trim () является именем переменной: см. Вызов функции trim () внутри test_trim (). В рамках trim (), вызываемой из test_trim (), $ 1 расширяется до foo, а $ {! 1} расширяется до $ foo (то есть до текущего содержимого переменной foo). Поищите в руководстве по bash 'переменная косвенность'.
flabdablet 9.04.2015 12:54:14
Как насчет этой небольшой модификации, чтобы поддержать обрезку нескольких переменных в одном вызове? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
Gene Pavlovsky 3.07.2016 09:37:23
@ AquariusPower нет необходимости использовать echo в подоболочке для однострочной версии, просто read -rd '' str <<<"$str"подойдет.
flabdablet 3.07.2016 19:08:38

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

Это удаляет завершающие пробелы и другие невидимые символы.

3
26.10.2012 19:36:42

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

$ x=" a z     e r ty "
$ echo "START[${x// /}]END"
START[azerty]END
2
25.09.2015 18:51:05
Этот подход (а) также удаляет внутренние пространства и (б) удаляет только пробелы , а не другие формы пустого пространства.
mklement0 4.06.2012 14:49:42
@ mklement0 Иногда это именно то, что нужно, хотя! :-)
Ville 19.02.2017 05:46:51
@Ville: Да, и с помощью моего разъясняющего поведение комментария будущие читатели могут решить, подходит ли им это решение.
mklement0 19.02.2017 06:08:28

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
3
25.09.2015 18:53:26

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

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

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

5
18.09.2012 11:24:14

Я бы просто использовал sed:

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

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

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|

в) Последнее замечание:
если вам не нравится использовать функцию, для однострочной строки вы можете просто использовать команду «легче запомнить», например:

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
}'
7
25.09.2015 18:55:32
Примечание: как предложено @mkelement, он не будет работать для многострочных строк, хотя он должен работать для однострочных строк.
GuruM 18.09.2012 10:18:01
Вы не правы: он работает и на многострочных строках. Просто
Luca Borrione 18.09.2012 12:36:50
+1 за использование - мне было легко протестировать код. Однако код по-прежнему не будет работать для многострочных строк. Если вы внимательно посмотрите на вывод, вы заметите, что все начальные / конечные внутренние пробелы также удаляются, например, пространство перед «многострочным» заменяется на «многострочное». Просто попробуйте увеличить количество пробелов в каждой строке.
GuruM 18.09.2012 14:16:45
Теперь я понимаю, что вы имеете в виду! Спасибо за голову, я отредактировал мой ответ.
Luca Borrione 18.09.2012 15:04:38
@ "Luca Borrione" - добро пожаловать :-) Не могли бы вы объяснить синтаксис sed, который вы используете в trim ()? Это также может помочь любому пользователю вашего кода настроить его для других целей. Также это может даже помочь найти крайние случаи для регулярного выражения.
GuruM 19.09.2012 06:22:43

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

echo "   lol  " | xargs

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

Примечание: это не удаляет все внутренние пробелы, поэтому "foo bar"остается неизменным; это НЕ становится "foobar". Тем не менее, несколько пробелов будут сжаты в единичные пробелы, поэтому "foo bar"станет "foo bar". Кроме того, он не удаляет символы конца строки.

946
27.02.2020 17:06:17
Ницца. Это работает очень хорошо. Я решил xargs echoпередать это просто, чтобы быть многословным о том, что я делаю, но xargs сам по себе будет использовать echo по умолчанию.
Will 18.02.2013 18:59:11
Хороший трюк, но будьте осторожны, вы можете использовать его для однострочной строки, но - благодаря дизайну xargs - он не просто будет обрабатывать многострочное содержимое. Сед твой друг тогда.
Jocelyn delalande 28.11.2013 09:57:07
Единственная проблема с xargs состоит в том, что он вводит новую строку, если вы хотите, чтобы новая строка была отключена, я бы порекомендовал sed 's/ *$//'в качестве альтернативы. Вы можете увидеть xargsновую строку следующим образом: echo -n "hey thiss " | xargs | hexdump вы заметите, 0a73что aэто новая строка. Если вы сделаете то же самое с sed: echo -n "hey thiss " | sed 's/ *$//' | hexdumpвы увидите 0073, нет новой строки.
user4401178 7.01.2015 04:30:45
Осторожный; это сломается, если строка в xargs содержит лишние пробелы между ними. Как «это один аргумент». xargs разделит на четыре.
bos 24.01.2015 23:12:14
Это плохо. 1. Это превратится a<space><space>bв a<space>b. 2. Еще больше: оно превратится a"b"c'd'eв abcde. 3. Еще больше: он не включится a"bи т. Д.
Sasha 11.02.2015 23:20:27

Хотя это не совсем Bash, он будет делать то, что вы хотите, и даже больше:

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

Если вы тоже хотите сделать его строчным, выполните:

php -r '$x = trim("  Hi There  "); $x = strtolower($x) ; echo $x;'
-2
25.09.2015 18:57:38
var="  a b  "
echo "$(set -f; echo $var)"

>a b
2
28.02.2013 00:35:37

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

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'
39
25.09.2015 18:58:17
Это объединяет несколько смежных пространств в одно.
Evgeni Sergeev 16.12.2013 06:13:52
Вы пробовали это, когда fooсодержит подстановочный знак? например, foo=" I * have a wild card"... сюрприз! Более того, это объединяет несколько смежных пространств в одно.
gniourf_gniourf 28.04.2014 08:47:02
Это отличное решение, если вы: 1. не хотите пробелов на концах 2. хотите, чтобы между каждым словом был только один пробел 3. работаете с контролируемым вводом без подстановочных знаков. По сути, он превращает плохо отформатированный список в хороший.
musicin3d 19.11.2015 15:25:40
Хорошее напоминание о подстановочных знаках @gniourf_gniourf +1. Все еще превосходное решение, Вамп. +1 тебе тоже.
Dr Beco 21.03.2016 01:05:08