2010-07-19 6 views
7

Je suis nouveau à UNIX, ayant seulement commencé à travailler aujourd'hui, mais l'expérience avec Java, et ont le code suivant:Vérification d'une chaîne pour voir si elle contient le caractère numérique sous UNIX

#/bin/bash 
echo "Please enter a word:" 
read word 
grep -i $word $1 | cut -d',' -f1,2 | tr "," "-"> output 

Cela fonctionne bien, mais ce que je dois maintenant faire est de vérifier quand le mot est lu, qu'il ne contient que des lettres et s'il a des caractères numériques dans l'impression "Invalid input!" message et demandez-leur d'y entrer à nouveau. J'ai supposé que les expressions régulières avec une instruction if seraient le moyen le plus simple de le faire, mais je n'arrive pas à comprendre comment les utiliser sous UNIX car je suis habitué à l'application Java. Toute aide à ce sujet serait grandement appréciée, car je ne pouvais pas trouver d'aide lors de la recherche car toutes les solutions avec des expressions régulières dans linux que je trouvais traitées uniquement si elles étaient toutes numériques ou non.

Répondre

17

Encore une autre approche. Grep sort avec 0 si une correspondance est trouvée, vous pouvez tester le code de sortie:

echo "${word}" | grep -q '[0-9]' 
if [ $? = 0 ]; then 
    echo 'Invalid input' 
fi 

Ceci est /bin/sh compatible.


Intégrer les suggestions de Daenyth et John, cela devient

if echo "${word}" | grep '[0-9]' >/dev/null; then 
    echo 'Invalid input' 
fi 
+3

'-q' pour grep n'est pas portable en dehors de GNU.Si vous voulez une portabilité totale (la seule raison d'utiliser sh), utilisez '>/dev/null 2> & 1' – Daenyth

+0

+1, c'est sympa, vraiment une solution UNIX, simple et propre. – Anders

+0

@Daenyth vous avez absolument raison, et j'ai même fait cela quand je l'ai essayé sur mon système, puis ajouté le '-q' quand j'ai posté ma réponse. Il semble que je sois GNU depuis trop longtemps. –

0

Un portable (en supposant bash> = 3) façon de le faire est de supprimer tous les numéros et tests pour la longueur:

#!/bin/bash 
read -p "Enter a number" var 
if [[ -n ${var//[0-9]} ]]; then 
    echo "Contains non-numbers!" 
else 
    echo "ok!" 
fi 

venant de Java, il est important de noter que bash n'a pas de concept réel d'objets ou types de données. Tout est une chaîne, et les structures de données complexes sont au mieux douloureuses.

Pour plus d'informations sur ce que j'ai fait, et d'autres fonctions connexes, google pour bash manipulation de chaînes.

+1

En supposant que bash kitty? C'est cruel. – MikeD

7

L'opérateur double bracket est une version étendue de la commande test qui prend en charge regexes via l'opérateur =~:

#!/bin/bash 

while true; do 
    read -p "Please enter a word: " word 
    if [[ $word =~ [0-9] ]]; then 
     echo 'Invalid input!' >&2 
    else 
     break 
    fi 
done 

Ceci est une caractéristique spécifique à bash. Bash est un shell plus récent qui n'est pas disponible sur toutes les versions d'UNIX - bien que par "newer" je veux dire "récemment développé à l'ère du tube post-vacuum" et par "pas toutes les saveurs d'UNIX" de Solaris et HP-UX. À mon avis, c'est l'option la plus simple et bash est beaucoup portable de nos jours, mais si être portable sur les anciennes UNIX est en fait important, alors vous aurez besoin d'utiliser les réponses sh-compatible des autres affiches. sh est le shell le plus commun et le plus largement supporté, mais le prix que vous payez pour la portabilité est en train de perdre des choses comme =~.

3

Si vous essayez d'écrire du code shell portable, vos options de manipulation de chaînes sont limitées. Vous pouvez utiliser des modèles englobement shell (qui sont beaucoup moins expressif que regexps) dans la case construction:

export LC_COLLATE=C 
read word 
while 
    case "$word" in 
    *[!A-Za-z]*) echo >&2 "Invalid input, please enter letters only"; true;; 
    *) false;; 
    esac 
do 
    read word 
done 

EDIT: réglage LC_COLLATE est nécessaire parce que dans la plupart des C non locales, les plages de caractères comme A-Z n » t avoir le sens "évident". Je suppose que vous voulez seulement des lettres ASCII; Si vous souhaitez également des lettres avec signes diacritiques, ne modifiez pas LC_COLLATE et remplacez A-Za-z par [:alpha:] (donc le motif entier devient *[![:alpha:]]*).

Pour les expressions rationnelles complètes, voir la commande expr. EDIT: Notez que expr, comme plusieurs autres outils shell de base, a des pièges avec certaines chaînes spéciales; les caractères z ci-dessous empêchent $word d'être interprétés comme des mots réservés par expr.

export LC_COLLATE=C 
read word 
while expr "z$word" : 'z[A-Za-z]*$' >/dev/null; then 
    echo >&2 "Invalid input, please enter letters only" 
    read word 
fi 

Si vous ciblez seulement assez récentes versions de bash, il existe d'autres options, telles que l'opérateur =~ des commandes conditionnelles [[ ... ]].

Notez que votre dernière ligne a un bug, la première commande doit être

grep -i "$word" "$1" 

Les citations sont parce que quelque peu contre-intuitive, "$foo" signifie « la valeur de la variable appelée foo » alors plaine $foo signifie « prenez la valeur de foo, divisez-la en mots séparés où elle contient des espaces, et traitez chaque mot comme un motif de globulation et essayez de l'agrandir ". (En fait, si vous avez déjà vérifié que $word ne contient que des lettres, laisser les guillemets ne fera pas de mal, mais il faut plus de temps pour penser à ces cas spéciaux que pour mettre les guillemets à chaque fois.)

+0

Le cas que vous avez répertorié échoue pour les entrées non-ascii non numériques. – Daenyth

+0

@Daenyth: true, toutes les solutions utilisant 'A-Za-z' supposent une locale ASCII. Alors laissez-moi ajouter une note de bas de page: si vous voulez autoriser toutes les lettres dans votre locale (y compris les lettres avec des signes diacritiques), remplacez 'A-Za-z' par' [: alpha:] 'partout (' case', 'expr' , 'grep', ...) (oui, vous aurez des parenthèses entre parenthèses). Si vous voulez seulement des lettres ASCII, mettez 'export LC_COLLATE = C' près du début de votre script. – Gilles

+0

La solution la plus simple consiste simplement à l'inverser - vérifiez qu'elle contient '[^ 0-9]'. Une liste blanche est plus facile qu'une liste noire. – Daenyth

0

Jouer avec l'expansion des paramètres Bash et les classes de caractères:

# cf. http://wiki.bash-hackers.org/syntax/pe 

word="abc1def" 
word="abc,def" 
word=$'abc\177def' 
# cf. http://mywiki.wooledge.org/BashFAQ/058 (no NUL byte in Bash variable) 
word=$'abc\000def' 
word="abcdef" 

(
set -xv 
[[ "${word}" != "${word/[[:digit:]]/}" ]] && echo invalid || echo valid 
[[ -n "${word//[[:alpha:]]/}" ]] && echo invalid || echo valid 
) 
1

Encore une autre (tout à fait) de manière portable pour le faire. ..

if test "$word" != "`printf "%s" "$word" | tr -dc '[[:alpha:]]'`"; then 
    echo invalid 
fi 
0

Les réponses de tout le monde semblent être basées sur le fait que les seuls caractères invalides sont des nombres. Les questions initiales indiquent qu'ils doivent vérifier que la chaîne ne contient "que des lettres".

Je pense que la meilleure façon de le faire est

nonalpha=$(echo "$word" | sed 's/[[:alpha:]]//g') 
if [[ ${#nonalpha} -gt 0 ]]; then 
    echo "Invalid character(s): $nonalpha" 
fi 

Si vous avez trouvé cette page à la recherche d'un moyen de détecter des caractères non numériques dans votre chaîne remplacer [[(comme moi!): Alpha: ]] avec [[: digit:]].