Ciąg argumentu do liczby całkowitej w bash

73

Próbując dowiedzieć się, jak przekonwertować argument na liczbę całkowitą w celu wykonania arytmetyki, a następnie wydrukować go, powiedz dla addOne.sh:

echo $1 + 1
>>sh addOne.sh 1
prints 1 + 1
użytkownik135986
źródło
Próbuję sformatować twoje pytanie, ale pozostaje niejasne. printf "1 + %s\n" $1 won't do ?
Archemar

Odpowiedzi:

87

W bash nie wykonuje się „konwersji argumentu na liczbę całkowitą w celu wykonania arytmetyki”. W bash zmienne są traktowane jako liczba całkowita lub ciąg znaków w zależności od kontekstu.

Aby wykonać arytmetykę, należy wywołać arytmetyczny operator rozszerzenia $((...)). Na przykład:

$ a=2
$ echo "$a + 1"
2 + 1
$ echo "$(($a + 1))"
3

lub ogólnie preferowane:

$ echo "$((a + 1))"
3

Należy pamiętać, że bash (w przeciwieństwie do ksh93, zsh lub yash) wykonuje tylko arytmetykę liczb całkowitych . Jeśli masz liczby zmiennoprzecinkowe (liczby dziesiętne), możesz skorzystać z innych narzędzi. Na przykład użyj bc:

$ b=3.14
$ echo "$(($b + 1))"
bash: 3.14 + 1: syntax error: invalid arithmetic operator (error token is ".14 + 1")
$ echo "$b + 1" | bc -l
4.14

Lub możesz użyć powłoki z obsługą arytmetyki zmiennoprzecinkowej zamiast bash:

zsh> echo $((3.14 + 1))
4.14
John1024
źródło
Używanie $ (()) lub (()) jest niebezpieczne, jeśli nie wiesz, jakie są ciągi znaków (np. Dane wejściowe użytkownika). Rozważ to: foo = foo ((foo + = 0)) Spowoduje to awarię skryptu podczas próby rekurencyjnej oceny foo. To samo z: foo = foo foo = $ ((foo + 0))
art.
12

W inny sposób możesz użyć expr

Dawny:

$ version="0002"
$ expr $version + 0
2
$ expr $version + 1
3
Nam
źródło
10

W bashmożna wykonać konwersję z dowolnej wartości na liczbę całkowitą za pomocą printf -v :

printf -v int '%d\n' "$1" 2>/dev/null

Liczba zmiennoprzecinkowa zostanie przekonwertowana na liczbę całkowitą, a nic nie będzie wyglądać tak, jakby liczba została przekonwertowana na 0. Potęgowanie zostanie obcięte do liczby przed e

Przykład:

$ printf -v int '%d\n' 123.123 2>/dev/null
$ printf '%d\n' "$int"
123
$ printf -v int '%d\n' abc 2>/dev/null
$ printf '%d\n' "$int"
0
$ printf -v int '%d\n' 1e10 2>/dev/null
$ printf '%d\n' "$int"
1
Cuonglm
źródło
2
W innych pociskach bez printf -vtego można osiągnąć dzięki zastępowaniu poleceń:int="$(printf '%d' 123.123 2>/dev/null)"
Adrian Günter,
2
To nie działa w bash.
Michael Martinez
@MichaelMartinez jesteś pewien? Jakiej bashużywasz wersji ?
cuonglm
GNU bash, wersja 4.2.46 (1) -release (x86_64-redhat-linux-gnu)
Michael Martinez
5

Podobna sytuacja pojawiła się ostatnio podczas opracowywania skryptów bash do uruchamiania zarówno w środowiskach Linux, jak i OSX. Wynik jednej komendy w OSX zwrócił ciąg zawierający kod wyniku; tj " 0". Oczywiście nie udało się poprawnie przetestować w następującym przypadku:

if [[ $targetCnt != 0 ]]; then...

Rozwiązaniem było wymuszenie (tj. „Konwersja”) wyniku na liczbę całkowitą, podobną do odpowiedzi @ John1024 powyżej, aby działał zgodnie z oczekiwaniami:

targetCnt=$(($targetCnt + 0))
if [[ $targetCnt != 0 ]]; then...
JESii
źródło
3
==itp. w [[(również [aka test) zrobić porównanie ciągów. Istnieją różne operatory dla porównania arytmetyczne, np [[ $targetcnt -ne 0 ]]; zobacz manpage (lub info) w wyrażeniach warunkowych. Aby przyciąć spacje konkretnie, możesz użyć niewymienionego [rozszerzenia zmiennej, [ $targetcnt == 0 ]aby uzyskać domyślne dzielenie słów (NIE zrobione w [[), ale ogólnie takie podejście prowadzi do niebezpieczeństwa.
dave_thompson_085
-2

dbają o kody kolorów, nawet w trace ( -x) nie będą się pojawiać, co by je rozdawało, to że ciąg, który ma być liczbą, jest zawijany w cudzysłów, bez względu na to, jak go wydrukujesz.

odtąd
źródło
1
Głosowałem w dół, ponieważ odpowiedź jest nieco niejasna. Być może mógłbyś dodać do czego skrypt powinien zostać poprawiony?
Time4Tea,
to była najwyższa odpowiedź w poszukiwaniu bash + liczba całkowita + ciąg, więc dodałem związaną z tym informację, która nie została uwzględniona w odpowiedziach, to znaczy, gdy łańcuchy są owinięte kolorowymi kodami, może nie być jasne, dlaczego operacje takie jak $((var+var))nawet nie powiodły się choć jeśli ty echolub printfobaj jesteście tymi samymi. Nie znam tej poprawki, ponieważ mogłem ją naprawić, wyłączając kody kolorów u źródła danych wyjściowych. Aby wykryć to w dziennikach śledzenia, zobaczysz przypisaną zmienną var='0', która powinna być po prostu taka, jaka powinna byćvar=0
przed
Co możesz zrobić, to upewnić się, że dane wyjściowe nie mają kodów kolorów, sedjeśli nie możesz go wyłączyć
przed