Como afirmar que uma sequência tem um caractere de nova linha e, se houver, remova-a

9

Eu tenho uma string que é o resultado de alguma operação sobre a qual não tenho controle. Quando imprimo essa variável usando echo, recebo:

echo $myvar
hello

No entanto, quando eu faço

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

Eu sempre entendo que eles não são iguais. Eu suspeito que isso é por causa de um newlinepersonagem.

A corda também se comporta de maneira estranha. Quando eu faço:

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

Eu recebo:

hellois my var twice: hello

Como posso verificar se isso é de fato devido a um newlinee, em caso afirmativo, removê-lo?

farid99
fonte
1
No Bash, você pode printf '%q\n' "$string"obter uma versão escapada de qualquer string. Por exemplo: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
l0b0
1
Você não está citando a expansão de nenhuma de suas variáveis. Se eles tivessem algum espaço em branco à direita, você não o veria echo $foo. Faça em echo "$foo"vez disso.
22826 Peter Cordes

Respostas:

9

O problema é que você tem um retorno de carro incorporado (CR \r). Isso faz com que o ponto de inserção de texto do terminal volte ao início da linha que está imprimindo. É por isso que você está vendo o 'olá' no início da linha no $newVARexemplo - sed -n lexibe uma visualização legível de caracteres não imprimíveis (e final de linha).

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

Você pode testá-lo com uma verificação simples da condição do bash:

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

Você pode combinar o teste e corrigir em uma etapa testando \r(s) e removendo-os via:

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

A correção usa a expansão de parâmetro do shell . O formulário específico usado acima é para substituir substrings com base no seu padrão fornecido: ${parameter/pattern/string}<- Isso substitui apenas o primeiro padrão encontrado pela string na variável denominada * parâmetro. Para substituir todos os padrões, basta alterar o primeiro /para //.

Peter.O
fonte
você poderia explicar seu último pedaço de código? a fix="....linha?
precisa saber é
@ farid99: explicação adicional para resposta, Nota fixpode ser varem si - ou muitas vezes você pode apenas usar a expansão de parâmetros como está, sem a necessidade de transferir o (possivelmente) valor modificado ..
Peter.O
5

Você pode representar \rcomo $'\r'no bash:

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

Ou pique o último \rem myvar:

if [ "${myvar%$'\r'*}" = "hello" ]; then
    echo they are equal
else
    echo they are not equal
fi
yaegashi
fonte
3

Curiosamente, em muitas conchas getoptsé um candidato muito provável para um trabalho como este. Isso pode parecer contra-intuitivo no começo, mas se você considerar que getopts'a função principal é reconhecer e oferecer a interpretação tantas opções de linha de comando de um caractere especificadas quanto as encontradas em uma série concatenada do mesmo, pode começar a se tornar um pouco mais sentido.

Para demonstrar, a partir de um bashshell:

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'

Dessa forma, às vezes pode ser conveniente permitir getoptslidar com a desmontagem como uma espécie de piloto automático do shell para casos como este. Ao fazer isso, você pode apenas filtrar bytes indesejados com / caseou [testar ]e criar seu backup de string a partir do byte 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'

Dado este exemplo de caso simples - e considerando um shell que suporta as expansões de parâmetros já mencionadas em outros lugares -, as expansões provavelmente servirão melhor aqui. Mas pensei getoptsque valeria a pena mencionar também, caso você não estivesse ciente de suas capacidades nesse aspecto. Certamente, quando soube disso, encontrei muitos aplicativos úteis para ele, de qualquer maneira.

mikeserv
fonte
0

Embora o Bash e outras linguagens de shell sejam úteis, às vezes é melhor usar uma linguagem de script verdadeira - como Perl. O Perl pode substituir scripts shell que chamam outros idiomas como sed e awk, bem como comandos UNIX com bastante facilidade. Aprendi isso há mais de 20 anos ao escrever scripts C-Shell que, por sua vez, chamavam sed, awk e vários comandos UNIX - antes de chamar o código FORTRAN. No Perl eu faria:

chomp($myvar);   # removes the newline char

if("$myvar" eq "hello")   # string comparison
  {
  print "they are equal\n";
  }
else
  {
  print "they are not equal\n";
  }
Pedro
fonte