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

Я хотел бы проверить, начинается ли строка с "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

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

+615
источник поделиться
13 ответов

Этот фрагмент в 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).

+870
источник

Если вы используете недавний 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.

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

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

+170
источник
другие ответы

Связанные вопросы


Похожие вопросы

Я всегда стараюсь использовать 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 "[email protected]"; 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, поэтому нужно использовать строки. Но вызов функций вложенности с помощью строк невозможно, трубы невозможны...)

+106
источник

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

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

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

if [[ $HOST == user1 || $HOST == node* ]]
+68
источник

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

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

Edit:

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

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

if [[ $HOST == user1 || $HOST == node* ]];
+55
источник

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

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

и

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

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

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

+29
источник

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

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "skip comment line"
fi
+28
источник

@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
+8
источник

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

Выражение

$HOST == node*

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

$HOST == "node"*

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

+8
источник
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
+6
источник

Я подправил ответ @markrushakoff, чтобы сделать его вызываемой функцией:

function yesNo {
  # prompts user with $1, returns true if response starts with y or Y or is empty string
  read -e -p "
$1 [Y/n] " YN

  [[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}

Используйте это так:

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] Y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] yes
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n]
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] ddddd
false

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

function toLowerCase {
  echo "$1" | tr '[:upper:]' '[:lower:]'
}

function yesNo {
  # $1: user prompt
  # $2: default value (assumed to be Y if not specified)
  # Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string

  local DEFAULT=yes
  if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
  if [[ "$DEFAULT" == y* ]]; then
    local PROMPT="[Y/n]"
  else
    local PROMPT="[y/N]"
  fi
  read -e -p "
$1 $PROMPT " YN

  YN="$( toLowerCase "$YN" )"
  { [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}

Используйте это так:

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N]
false

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N] y
true

$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false
+1
источник

grep

Забывая о производительности, это POSIX и выглядит лучше, чем решения для case:

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

Объяснение:

0
источник

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

-5
источник

Посмотрите другие вопросы по меткам или Задайте вопрос