В 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

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

if [[ $HOST == user1 || $HOST == node* ]]

Ответ 5

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

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

Edit:

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

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

if [[ $HOST == user1 || $HOST == node* ]];

Ответ 6

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

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

и

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

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

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

Ответ 7

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

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "skip comment line"
fi

Ответ 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

Ответ 9

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

Выражение

$HOST == node*

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

$HOST == "node"*

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

Ответ 10

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

Ответ 11

Я подправил ответ @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

Ответ 12

grep

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

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

Объяснение:

Ответ 13

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