2010-07-19 9 views
7

Jestem nowy na UNIX, mając tylko zaczął go na dzisiaj pracy, ale doświadczenie z Java i mają następujący kod:Sprawdzanie ciąg aby sprawdzić, czy zawiera ona postać numeryczną w UNIX

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

to działa W porządku, ale teraz muszę sprawdzić, czy odczytano słowo, czy zawiera ono tylko litery i czy ma znaki numeryczne w druku "Nieprawidłowe dane wejściowe!" i poproś o ponowne wprowadzenie. Zakładam, że wyrażenia regularne z instrukcją if byłyby prostym sposobem na zrobienie tego, ale nie mogę zrozumieć, jak używać ich w systemie UNIX, ponieważ jestem przyzwyczajony do ich aplikacji Java. Jakakolwiek pomoc w tym przypadku byłaby ogromnie doceniona, ponieważ nie mogłem znaleźć pomocy przy wyszukiwaniu jako wszystkich rozwiązań z wyrażeń regularnych w linuxie, które znalazłem tylko w przypadku, gdy był albo numeryczny, albo nie.

Odpowiedz

17

Jeszcze inne podejście. Grep wychodzi z 0 jeśli zostanie znaleziony, dzięki czemu można przetestować kod wyjścia:

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

To jest /bin/sh kompatybilne.


Zawierające Daenyth i Jana sugestie, staje

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

'-q' na grep nie jest przenośne poza GNU.Jeśli chcesz mieć pełną przenośność (jedyny powód, aby kiedykolwiek używać sh), użyj '>/dev/null 2> & 1' – Daenyth

+0

+1, To jest ładne, naprawdę rozwiązanie UNIX, proste i czyste. – Anders

+0

@Daenyth masz absolutną rację, a nawet zrobiłem to, kiedy wypróbowałem to w moim systemie, a następnie dodałem '-q', kiedy opublikowałem swoją odpowiedź. Wydaje mi się, że byłem GNU-tylko zbyt długo. –

0

Jeden przenośny (zakładając bash> = 3) sposób to zrobić, to usunąć wszystkie numery i test na długości:

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

Coming od Javy, ważne jest, aby pamiętać, że bash nie ma realnego pojęcia obiektów lub typy danych. Wszystko jest ciągiem, a skomplikowane struktury danych są w najlepszym wypadku bolesne.

Aby uzyskać więcej informacji o tym, co zrobiłem i inne powiązane funkcje, google do manipulacji ciągami bash.

+1

Zakładając kotka bash? To okrutne. – MikeD

7

podwójny wspornik operatora jest rozbudowana wersja podporządkowania test który wspiera regexes przez operatora =~:

#!/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 

Jest to cecha atakujących specyficzne. Bash to nowsza powłoka, która nie jest dostępna we wszystkich wersjach systemu UNIX - chociaż przez "nowszą" mam na myśli "dopiero niedawno opracowaną w erze po próżni" i "nie wszystkie smaki UNIX" mam na myśli relikty takie jak stare wersje Solaris i HP-UX.

Moim zdaniem jest to najprostsza opcja, a bash jest obecnie dużo przenośny, ale jeśli przenośne do starych UNIXes jest w rzeczywistości ważne, to musisz użyć odpowiedzi kompatybilnych z innymi plakatami. sh jest najbardziej rozpowszechnioną i najczęściej wspieraną powłoką, ale cena, którą płacisz za przenoszenie, traci na wartościach takich jak =~.

3

Jeśli próbujesz napisać przenośny kod powłoki, opcje manipulowania ciągami są ograniczone. Można użyć shell patterns globbing (które są o wiele mniej wyrazisty niż wyrażeń regularnych) w case konstruktem:

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: ustawienie LC_COLLATE jest konieczne, ponieważ w większości spoza C lokalizacjach, charakter waha jak A-Z don” t mają "oczywiste" znaczenie. Zakładam, że chcesz tylko litery ASCII; jeśli chcesz również litery z znakami diakrytycznymi, nie zmieniaj wartości LC_COLLATE i zastępuj A-Za-z przez [:alpha:] (aby cały wzór stał się *[![:alpha:]]*).

Aby uzyskać pełne wyliczenia, zobacz komendę expr. EDYCJA: Zauważ, że expr, podobnie jak kilka innych podstawowych narzędzi powłoki, ma pułapki ze specjalnymi ciągami znaków; Poniższe znaki z uniemożliwiają interpretację $word jako słów zarezerwowanych przez 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 

Jeśli tylko cel wystarczającej ilości aktualnych wersji bash, istnieją inne opcje, takie jak operator =~ z [[ ... ]] poleceń warunkowych.

pamiętać, że ostatnia linia ma błąd, pierwsza komenda powinna być

grep -i "$word" "$1" 

Cytaty są dlatego nieco wbrew intuicji, "$foo" oznacza „wartość zmiennej o nazwie foo”, podczas gdy zwykły $foo pomocą „ weź wartość foo, podziel ją na oddzielne słowa, gdzie zawiera białe znaki, i traktuj każde słowo jako wzór globalny i spróbuj go rozwinąć ". (W rzeczywistości, jeśli już sprawdziłeś, że $word zawiera tylko litery, pozostawienie cytatów nie zaszkodzi, ale potrzeba więcej czasu, aby pomyśleć o tych specjalnych przypadkach, niż po prostu wstawiać cytaty za każdym razem.)

+0

Przypadek, który podałeś, nie działa dla nie-numerycznego wejścia nie-ASCII. – Daenyth

+0

@Daenyth: true, wszystkie rozwiązania używające 'A-Za-z' przyjmują ustawienia ASCII. Dodajmy więc przypis: jeśli chcesz zezwolić na wszystkie litery w twoim języku (w tym litery z znakami diakrytycznymi), zamień 'A-Za-z' na' [: alpha:] 'wszędzie (' case', 'wyrażenie' , 'grep', ...) (tak, będziesz miał nawiasy w nawiasach). Jeśli chcesz tylko litery ASCII, umieść 'export LC_COLLATE = C' blisko początku skryptu. – Gilles

+0

Prostszym rozwiązaniem jest po prostu odwrócenie go - sprawdź, czy zawiera on '[^ 0-9]'. Biała lista jest łatwiejsza niż czarna lista. – Daenyth

0

zabawy z Bash klas rozszerzających parametr i znaków:

# 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

Jeszcze inna (dość) przenośny sposób to zrobić. ..

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

Wydaje się, że wszystkie odpowiedzi są oparte na fakcie, że jedynymi błędnymi znakami są liczby. Początkowe pytania mówią, że muszą sprawdzić, czy ciąg zawiera "tylko litery".

Myślę, że najlepszym sposobem na to jest

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

Jeśli znalazłeś tę stronę szuka sposobu wykrywania znaków spoza numerycznych w ciąg zastąpić [[(jak ja!): Alpha: ]] z [[: cyfr:]].

Powiązane problemy