สตริงอาร์กิวเมนต์เป็นจำนวนเต็มใน bash


74

พยายามหาวิธีแปลงอาร์กิวเมนต์เป็นจำนวนเต็มเพื่อดำเนินการทางคณิตศาสตร์แล้วพิมพ์ออกมาพูดเพื่อaddOne.sh:

echo $1 + 1
>>sh addOne.sh 1
prints 1 + 1

ฉันพยายามจัดรูปแบบคำถามของคุณ แต่ก็ยังไม่ชัดเจน printf "1 + %s\n" $1 won't do ?
Archemar

คำตอบ:


88

ใน bash ไม่มีใคร "แปลงอาร์กิวเมนต์เป็นจำนวนเต็มเพื่อดำเนินการทางคณิตศาสตร์" ใน bash ตัวแปรจะถือว่าเป็นจำนวนเต็มหรือสตริงขึ้นอยู่กับบริบท

$((...))ในการดำเนินการทางคณิตศาสตร์ที่คุณควรจะเรียกผู้ประกอบการขยายตัวทางคณิตศาสตร์ ตัวอย่างเช่น:

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

หรือที่ต้องการโดยทั่วไป:

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

คุณควรระวังว่า bash (ตรงข้ามกับ ksh93, zsh หรือ yash) ทำการคำนวณทางเลขจำนวนเต็มเท่านั้น หากคุณมีตัวเลขจุดลอยตัว (ตัวเลขที่มีทศนิยม) แสดงว่ามีเครื่องมืออื่น ๆ ที่จะช่วยเหลือ ตัวอย่างเช่นใช้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

หรือคุณสามารถใช้เชลล์ที่มีการสนับสนุนเลขคณิตของทศนิยมแทนการทุบตี:

zsh> echo $((3.14 + 1))
4.14

การใช้ $ (()) หรือ (()) ไม่ปลอดภัยหากคุณไม่ทราบว่าสตริงนั้นคืออะไร (เช่นอินพุตของผู้ใช้) พิจารณาสิ่งนี้: foo = foo ((foo + = 0)) สิ่งนี้จะทำให้สคริปต์ขัดข้องขณะที่พยายามประเมิน foo ซ้ำ ๆ เช่นเดียวกันกับ: foo = foo foo = $ ((foo + 0))
ศิลปะ


10

ในbashคุณสามารถทำการแปลงจากอะไรก็ได้เป็นจำนวนเต็มโดยใช้printf -v :

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

จำนวนลอยตัวจะถูกแปลงเป็นจำนวนเต็มในขณะที่สิ่งใดที่ดูเหมือนว่าตัวเลขจะถูกแปลงเป็น 0 การยกกำลังจะถูกปัดเศษเป็นจำนวนก่อน e

ตัวอย่าง:

$ 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

2
ในเชลล์อื่นที่ไม่มีprintf -vสิ่งนี้สามารถทำได้ด้วยการทดแทนคำสั่ง:int="$(printf '%d' 123.123 2>/dev/null)"
Adrian Günter

2
สิ่งนี้ใช้ไม่ได้กับการทุบตี
Michael Martinez

@MichaelMartinez คุณแน่ใจหรือไม่ bashคุณใช้เวอร์ชั่นอะไร
cuonglm

GNU ทุบตี, รุ่น 4.2.46 (1) - ปล่อย (x86_64-redhat-linux-gnu)
Michael Martinez

5

สถานการณ์ที่คล้ายกันเกิดขึ้นเมื่อเร็ว ๆ นี้เมื่อพัฒนาสคริปต์ทุบตีเพื่อให้ทำงานได้ทั้งในสภาพแวดล้อม Linux และ OSX ผลลัพธ์ของคำสั่งเดียวใน OSX จะส่งคืนสตริงที่มีรหัสผลลัพธ์ เช่น" 0". แน่นอนสิ่งนี้ไม่สามารถทดสอบได้อย่างถูกต้องในกรณีต่อไปนี้:

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

วิธีแก้ปัญหาคือการบังคับ (เช่น 'แปลง') ผลลัพธ์เป็นจำนวนเต็มคล้ายกับสิ่งที่ @ John1024 ตอบไว้ข้างต้นเพื่อให้ทำงานได้ตามที่คาดไว้:

targetCnt=$(($targetCnt + 0))
if [[ $targetCnt != 0 ]]; then...

3
==etc in [[( [หรือ aka test) ทำการเปรียบเทียบสตริง มีโอเปอเรเตอร์ต่าง ๆสำหรับการเปรียบเทียบทางคณิตศาสตร์เช่น[[ $targetcnt -ne 0 ]]; ดู manpage (หรือข้อมูล) ภายใต้นิพจน์เงื่อนไข เพื่อตัดช่องว่างโดยเฉพาะคุณสามารถใช้[กับการขยายตัวตัวแปร[ $targetcnt == 0 ]ที่ไม่ได้ระบุไว้เพื่อรับ wordplitting เริ่มต้น (ไม่ได้ทำใน[[) แต่โดยทั่วไปวิธีการที่จะนำคุณไปสู่อันตราย
dave_thompson_085

-2

ใส่ใจเกี่ยวกับรหัสสีแม้ในการติดตาม ( -x) พวกเขาจะไม่ปรากฏสิ่งที่จะให้มันออกมาก็คือสตริงที่ควรจะเป็นตัวเลขที่ถูกห่อด้วยคำพูดไม่ว่าคุณจะพิมพ์มัน


1
ลงคะแนนในขณะที่ฉันค้นหาคำตอบของคุณไม่ชัดเจน บางทีคุณสามารถเพิ่มสิ่งที่สคริปต์ควรแก้ไข?
Time4Tea

นี่เป็นคำตอบอันดับต้น ๆ ในการค้นหา bash + integer + string ดังนั้นฉันจึงเพิ่มข้อมูลที่เกี่ยวข้องซึ่งไม่ได้รวมอยู่ในคำตอบนั่นคือเมื่อสตริงถูกห่อด้วยรหัสสีมันอาจไม่ชัดเจนว่าทำไมการดำเนินการเช่น$((var+var))ล้มเหลว แม้ว่าคุณechoหรือprintfทั้งสองจะแตกต่างกัน ฉันไม่ทราบวิธีแก้ไขเนื่องจากฉันสามารถแก้ไขได้โดยการปิดการใช้งานรหัสสีที่แหล่งที่มาของผลลัพธ์ ในการสังเกตเห็นในบันทึกการติดตามคุณจะเห็นตัวแปรที่ได้รับมอบหมายเช่นเดียวกับvar='0'ที่ควรจะเป็นvar=0
untore

สิ่งที่คุณสามารถทำได้คือตรวจสอบให้แน่ใจว่าผลลัพธ์ไม่ได้มีรหัสสีsedหากคุณไม่สามารถปิดการใช้งานได้
จนถึง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.