В bash, как я могу проверить, начинается ли строка с некоторого значения?

560

Я хотел бы проверить, начинается ли строка с "node", например. "Node001". Что-то вроде

if [ $HOST == user* ]  
  then  
  echo yes  
fi

Как я могу сделать это правильно?


Мне также нужно объединить выражения, чтобы проверить, является ли HOST "user1" или начинается с "node"

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];  
then  
echo yes 
fi

> > > -bash: [: too many arguments

Как это сделать правильно?

  • 7
    Не поддавайтесь соблазну комбинировать выражения. Может показаться более уродливым иметь два отдельных условия, хотя вы можете дать более качественные сообщения об ошибках и упростить отладку сценария. Также я бы избегал функций bash. Переключатель это путь.
  • 0
    Возможный дубликат String содержит подстроку в Bash .
Теги:
string
comparison

11 ответов

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

Этот фрагмент в Advanced Bash Scripting Guide говорит:

# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.

[[ $a == z* ]]   # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

Итак, у вас это было почти правильно; вам нужны двойные скобки, а не отдельные скобки.


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

HOST=user1
if  [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes1
fi

HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes2
fi

Что будет echo

yes1
yes2
Синтаксис

Bash if трудно использовать (IMO).

  • 8
    Для регулярного выражения вы имеете в виду [[$ a = ~ ^ z. *]]?
  • 2
    Так есть ли функциональная разница между [[ $a == z* ]] и [[ $a == "z*" ]] ? Другими словами: они работают по-другому? А что конкретно вы имеете в виду, когда говорите "$ a равен z *"?
Показать ещё 9 комментариев
150

Если вы используете недавний bash (v3+), я предлагаю оператор сравнения bash regex =~, т.е.

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi

Чтобы сопоставить this or that иное в регулярном выражении, используйте | т.е.

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi

Обратите внимание - это "правильный" синтаксис регулярного выражения.

  • user* означает use и ноль или более вхождений r, поэтому use и userrrr будут совпадать.
  • user.* означает, что user и ноль-или-больше любых символов, так что user1, userX будет соответствовать.
  • ^user.* означает совпадение с шаблоном user.* в начале $ HOST.

Если вы не знакомы с синтаксисом регулярных выражений, попробуйте обратиться к этому ресурсу.

Обратите внимание - лучше, если вы будете задавать каждый новый вопрос как новый вопрос, это сделает stackoverflow более аккуратным и более полезным. Вы всегда можете включить ссылку на предыдущий вопрос для справки.

  • 0
    Спасибо, Брабстер! Я добавил к оригинальному сообщению новый вопрос о том, как комбинировать выражения в if cluase.
  • 1
    Жаль, что принятый ответ ничего не говорит о синтаксисе регулярных выражений.
Показать ещё 1 комментарий
94

Я всегда стараюсь использовать POSIX sh вместо использования расширений bash, так как одним из основных моментов написания сценария является переносимость. (Помимо подключения программ, не заменяя их)

В sh, есть простой способ проверить условие "префикс".

case $HOST in node*)
    your code here
esac

Учитывая, насколько старый, тайный и жестокий sh (и bash не является излечением: он более сложный, менее последовательный и менее портативный), я хотел бы отметить очень хороший функциональный аспект: хотя некоторые синтаксические элементы например case, полученные конструкторы не отличаются от любых других заданий. Они могут быть составлены таким же образом:

if case $HOST in node*) true;; *) false;; esac; then
    your code here
fi

Или даже короче

if case $HOST in node*) ;; *) false;; esac; then
    your code here
fi

Или даже короче (просто представить ! в качестве языкового элемента, но теперь это плохой стиль)

if ! case $HOST in node*) false;; esac; then
    your code here
fi

Если вам нравится быть явным, создайте свой собственный язык:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

Разве это не совсем хорошо?

if beginswith node "$HOST"; then
    your code here
fi

И так как sh - это в основном только задания и строковые списки (и внутренние процессы, из которых выполняются задания), мы можем теперь даже выполнить небольшое функциональное программирование:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }

all() {
    test=$1; shift
    for i in "$@"; do
        $test "$i" || return
    done
}

all "beginswith x" x xy xyz ; checkresult  # prints TRUE
all "beginswith x" x xy abc ; checkresult  # prints FALSE

Это элегантно. Не то, чтобы я защищал использование sh для чего-то серьезного - он слишком быстро ломается от требований реального мира (нет lambdas, поэтому нужно использовать строки. Но вызов функций вложенности с помощью строк невозможно, трубы невозможны...)

  • 8
    +1 Это не только портативный, но и читаемый, идиоматичный и элегантный (для сценария оболочки). Это также распространяется естественным образом на несколько моделей; case $HOST in user01 | node* ) ...
  • 0
    Есть ли название для этого типа форматирования кода? if case $HOST in node*) true;; *) false;; esac; then я видел это здесь и там, на мой взгляд, это выглядит как будто искаженное.
Показать ещё 8 комментариев
56

Вы можете выбрать только часть строки, которую вы хотите проверить:

if [ ${HOST:0:4} = user ]

Для вашего последующего вопроса вы можете использовать OR:

if [[ $HOST == user1 || $HOST == node* ]]
  • 0
    Спасибо Мартин! Я добавил к оригинальному сообщению новый вопрос о том, как комбинировать выражения в if cluase.
  • 6
    Вы должны заключить в кавычки ${HOST:0:4}
Показать ещё 1 комментарий
55

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

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

Edit:

Я добавил ваших заместителей к описанию дела выше

В вашей отредактированной версии у вас слишком много скобок. Он должен выглядеть следующим образом:

if [[ $HOST == user1 || $HOST == node* ]];
  • 0
    Спасибо, Деннис! Я добавил к оригинальному сообщению новый вопрос о том, как комбинировать выражения в if cluase.
  • 7
    «некоторым людям нравится ...»: этот вариант более переносим между версиями и оболочками.
Показать ещё 2 комментария
27

поскольку # имеет значение в bash, я получил следующее решение.
Кроме того, мне нравится лучше упаковывать строки с помощью "" для преодоления пробелов и т.д.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "skip comment line"
fi
  • 0
    Это было именно то, что мне было нужно. Спасибо!
  • 0
    спасибо, мне также было интересно, как сопоставить строку, начинающуюся с blah: похоже, это ответ!
24

Хотя я считаю, что большинство ответов здесь совершенно правильно, многие из них содержат ненужные базисы. Расширение параметров POSIX дает вам все, что вам нужно:

[ "${host#user}" != "${host}" ]

и

[ "${host#node}" != "${host}" ]

${var#expr} удаляет наименьший префикс, сопоставляющий expr с ${var} и возвращает это. Следовательно, если ${host} начинается не с user (node), ${host#user} (${host#node}) совпадает с ${host}.

expr позволяет fnmatch() подстановочные знаки, поэтому ${host#node??} и друзья также работают.

  • 1
    Я бы сказал, что bashism [[ $host == user* ]] может быть необходим, поскольку он гораздо более читабелен, чем [ "${host#user}" != "${host}" ] . Таким образом, при условии, что вы управляете средой, в которой выполняется скрипт (нацелены на последние версии bash ), предпочтительнее первое.
  • 1
    @ x-yuri Честно говоря, я бы просто упаковал это в функцию has_prefix() и больше никогда не смотрел на это.
8

@OP, для обоих вопросов вы можете использовать case/esac

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac

второй вопрос

case "$HOST" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$HOST" in
 node*|user) echo "ok";;
esac

ИЛИ Bash 4.0

case "$HOST" in
 user) ;& 
 node*) echo "ok";; 
esac
  • 0
    Обратите внимание, что ;& доступен только в Bash> = 4.
6
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];  
then  
echo yes 
fi

не работает, потому что все [, [[и test признают ту же нерекурсивную грамматику. см. раздел УСЛОВНЫЕ ЭКСПРЕССИИ на странице bash.

В стороне, SUSv3 говорит

Условная команда, основанная на KornShell (двойная скобка [[]]), была удалена из описания языка команд оболочки в раннем предложении. Возрастали возражения, что реальной проблемой является неправильное использование тестовой команды ([), а ее размещение в оболочке - неправильный способ устранения проблемы. Вместо этого достаточно правильной документации и нового зарезервированного слова оболочки (!).

Тесты, требующие нескольких тестовых операций, могут выполняться на уровне оболочки, используя отдельные вызовы тестовой команды и логики оболочки, вместо того, чтобы использовать флаг проверки -o.

вам нужно будет записать его таким образом, но тест не поддерживает его:

if [ $HOST == user1 -o $HOST == node* ];  
then  
echo yes 
fi

test uses = для равенства строк, что более важно, это не поддерживает соответствие шаблонов.

case/esac имеет хорошую поддержку для сопоставления с образцом:

case $HOST in
user1|node*) echo yes ;;
esac

у него есть дополнительное преимущество, которое не зависит от bash, синтаксис переносимый. из спецификации Single Unix, командный язык командной оболочки:

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac
  • 1
    [ и test как встроенные в Bash, так и внешние программы. Попробуйте type -a [ .
  • 0
    спасибо, отредактировано, надеюсь, к лучшему.
Показать ещё 1 комментарий
4

Добавление чуть более подробной информации о синтаксисе к ответу Марка Рушакова самого высокого ранга.

Выражение

$HOST == node*

Может также быть написано как

$HOST == "node"*

Эффект тот же. Просто убедитесь, что подстановочный знак находится за пределами цитируемого текста. Если подстановочный знак находится внутри кавычек, он будет интерпретирован буквально (т.е. не как подстановочный знак).

-5

Еще одна вещь, которую вы можете сделать, это cat из того, что вы эхом и трубкой с inline cut -c 1-1

Ещё вопросы

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