Comment affirmer qu'une chaîne a un caractère de retour à la ligne et, si oui, le supprimer

9

J'ai une chaîne qui est le résultat d'une opération sur laquelle je n'ai aucun contrôle. Lorsque j'imprime cette variable à l'aide echo, j'obtiens:

echo $myvar
hello

Cependant, quand je fais

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

J'ai toujours l'impression qu'ils ne sont pas égaux. Je soupçonne que c'est à cause d'un newlinepersonnage.

La chaîne se comporte également étrangement. Quand je fais:

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

Je reçois:

hellois my var twice: hello

Comment puis-je vérifier si cela est en fait dû à un newlineet, si oui, le supprimer?

farid99
la source
1
Dans Bash, vous pouvez printf '%q\n' "$string"obtenir une version échappée de n'importe quelle chaîne. Par exemple: printf '%q\n' 'foo\n'-> foo\\n; printf '%q\n' $'foo\n'->$'foo\n'
l0b0
1
Vous ne citez l'expansion d'aucune de vos variables. S'ils avaient un espace de fuite, vous ne le verriez pas avec echo $foo. Faites à la echo "$foo"place.
Peter Cordes

Réponses:

9

Le problème est que vous avez un retour chariot intégré (CR, \r). Cela provoque le point d'insertion de texte terminal au retour de la ligne qu'il imprime. C'est pourquoi vous voyez le «bonjour» au début de la ligne dans votre $newVARexemple - sed -n laffiche une vue lisible des caractères non imprimables (et à la fin de la ligne).

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

Vous pouvez le tester avec une simple vérification de l'état de bash:

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

Vous pouvez combiner le test et corriger en une seule étape en testant le \r(s) et en les supprimant via:

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

Le correctif utilise Shell Parameter Expansion . La forme particulière utilisée ci-dessus est pour remplacer les sous-chaînes en fonction de votre modèle fourni: ${parameter/pattern/string}<- Cela remplace uniquement le premier modèle trouvé avec une chaîne dans la variable nommée paramètre *. Pour remplacer tous les modèles, il vous suffit de remplacer le premier /par //.

Peter.O
la source
pourriez-vous expliquer votre dernier morceau de code? la fix="....ligne?
farid99
@ farid99: explication ajoutée pour répondre, la note fixpeut être varelle-même - ou souvent vous pouvez simplement utiliser l'expansion des paramètres telle
quelle
5

Vous pouvez représenter \rcomme $'\r'dans bash:

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

Ou coupez le dernier \ren myvar:

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

Curieusement, dans de nombreux obus getoptsest un candidat très probable pour un emploi comme celui-ci. Cela peut sembler contre-intuitif au premier abord, mais si vous considérez que getopts`` la fonction principale est de reconnaître et d'offrir à l'interprétation autant d'options de ligne de commande à caractère unique spécifiées que dans une série concaténée de la même chose, cela pourrait commencer à faire un peu plus de sens.

Pour démontrer, à partir d'un 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'

De cette façon, il peut parfois être pratique de permettre getoptsde gérer le démontage comme une sorte de pilote automatique de coque pour des cas comme celui-ci. Lorsque vous le faites, vous pouvez simplement éliminer les octets indésirables w / a caseou [tester ]et créer votre sauvegarde de chaîne à partir de l'octet 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'

Étant donné ce cas d'exemple simple - et étant donné un shell qui prend en charge les extensions de paramètres déjà mentionnées ailleurs - lesdites extensions vous serviront probablement mieux ici. Mais je pensais getoptsque cela mériterait également d'être mentionné au cas où vous ne seriez pas au courant de ses capacités à cet égard. Certes, quand je l'ai appris, j'ai trouvé de nombreuses applications utiles.

mikeserv
la source
0

Bien que Bash et d'autres langages shell soient pratiques, il est parfois préférable d'utiliser un vrai langage de script - comme Perl. Perl peut remplacer les scripts shell qui appellent d'autres langages tels que sed et awk ainsi que les commandes UNIX assez facilement. J'ai appris cela il y a plus de 20 ans lors de l'écriture de scripts C-Shell qui à leur tour appelaient sed, awk et diverses commandes UNIX - avant d'appeler du code FORTRAN. En Perl je ferais:

chomp($myvar);   # removes the newline char

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