Как утверждать, что строка имеет символ новой строки и, если это так, удалить его

9

У меня есть строка, которая является результатом какой-то операции, которую я не могу контролировать. Когда я печатаю эту переменную используя echo, я получаю:

echo $myvar
hello

Тем не менее, когда я делаю

if [ $myvar = "hello" ]; then
    echo they are equal
else
    echo they are not equal
fi

Я всегда понимаю, что они не равны. Я подозреваю, что это из-за newlineхарактера.

Строка также ведет себя странно. Когда я делаю:

newVAR="this is my var twice: "$myvar$myvar
echo $newVAR

Я получил:

hellois my var twice: hello

Как я могу проверить, действительно ли это происходит из-за a, newlineи, если да, удалить его?

farid99
источник
1
В Bash вы можете printf '%q\n' "$string"получить экранированную версию любой строки. Например: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
2010 года
1
Вы не цитируете расширение какой-либо из ваших переменных. Если бы у них были какие-то конечные пробелы, вы бы этого не увидели echo $foo. Делай echo "$foo"вместо этого.
Питер Кордес

Ответы:

9

Проблема в том, что у вас есть встроенный возврат каретки (CR, \r). Это приводит к тому, что точка вставки текста терминала возвращается к началу строки, которую она печатает. Вот почему вы видите «привет» в начале строки в вашем $newVARпримере - sed -n lотображает читаемое представление непечатаемых символов (и конец строки).

var=ab$'\r'c ; echo "$var";  printf %s "$var" | sed -n l
# output:
cb
ab\rc$

Вы можете проверить это с помощью простой проверки состояния bash:

[[ $var == *$'\r'* ]] && echo yes || echo no
# output:
yes

Вы можете объединить тестирование и исправление за один шаг, проверив \r(ы) и удалив их с помощью:

fix="${var//$'\r'/}"; echo "$var"; echo "$fix"
# output:
cb
abc

Затруднительное использует Shell Параметр Expansion . Конкретная форма, использованная выше, предназначена для замены подстрок на основе вашего предоставленного шаблона: ${parameter/pattern/string}<- это заменяет только первый найденный шаблон на строку в переменной с именем * параметр. Чтобы заменить все шаблоны, вам просто нужно изменить первый /на //.

Peter.O
источник
Не могли бы вы объяснить свой последний кусок кода? fix="....линия?
farid99
@ farid99: объяснение добавлено, чтобы ответить. Примечание fixможет быть varсамо по себе - или часто вы можете просто использовать расширение параметра как есть, без необходимости переназначать (возможно) измененное значение ..
Peter.O
5

Вы можете представить \rкак $'\r'в bash:

if [ "$myvar" = "hello"$'\r' ]; then
    echo they are equal
else
    echo they are not equal
fi

Или нарезать последний \rв myvar:

if [ "${myvar%$'\r'*}" = "hello" ]; then
    echo they are equal
else
    echo they are not equal
fi
yaegashi
источник
3

Любопытно, что во многих снарядах getopts очень вероятный кандидат на такую ​​работу. Поначалу это может показаться нелогичным, но если учесть, что getopts«основная функция заключается в распознавании и предложении для интерпретации стольких заданных односимвольных параметров командной строки, сколько может быть найдено в объединенной серии одинаковых, это может привести к некоторому больше смысла.

Для демонстрации из bashоболочки:

x=$(printf '\n\r%010s\t' hello)
OPTIND=1
while  getopts : na "-$x"
do     printf %q\\n "$OPTARG"
done

$'\n'
$'\r'
\
\
\
\
\
h
e
l
l
o
$'\t'

Таким образом, иногда бывает удобно разрешить getoptsобрабатывать разборку как своего рода автопилот в таких случаях. Когда вы сделаете это, вы можете просто отфильтровать нежелательные байты с помощью caseили [протестировать ]и создать резервную копию строки из байта 1:

OPTIND=1 y=$(printf \\n\\r) z=
while  getopts : na "-$x"
do     case $OPTARG in ([!$y])
            z=$z$OPTARG
       esac
done
printf %q\\n "$z"

$'     hello\t'

Учитывая этот простой пример, а также оболочку, которая поддерживает раскрытие параметров, уже упоминавшееся в другом месте, указанные расширения, вероятно, будут вам здесь полезнее. Но я подумал, getoptsчто стоит также упомянуть, если вы не знаете о его возможностях в этом отношении. Конечно, когда я узнал об этом, я нашел много полезных приложений для него, так или иначе.

mikeserv
источник
0

Хотя Bash и другие языки оболочки удобны, иногда лучше использовать настоящий язык сценариев, такой как Perl. Perl может легко заменить сценарии оболочки, которые вызывают другие языки, такие как sed и awk, а также команды UNIX. Я узнал об этом 20 с лишним лет назад, когда писал сценарии C-Shell, которые в свою очередь называли sed, awk и различные команды UNIX, прежде чем вызывать код FORTRAN. В Perl я бы сделал:

chomp($myvar);   # removes the newline char

if("$myvar" eq "hello")   # string comparison
  {
  print "they are equal\n";
  }
else
  {
  print "they are not equal\n";
  }
Питер
источник