วัตถุประสงค์ของ: (โคลอน) GNU Bash builtin คืออะไร


336

อะไรคือจุดประสงค์ของคำสั่งที่ไม่ทำอะไรเลยแม้แต่น้อยกว่าผู้นำความคิดเห็น แต่จริงๆแล้วคือเชลล์ในตัวและในตัวของมันเอง?

มันช้ากว่าการแทรกความคิดเห็นลงในสคริปต์ของคุณประมาณ 40% ต่อการโทรหนึ่งครั้งซึ่งอาจแตกต่างกันอย่างมากขึ้นอยู่กับขนาดของความคิดเห็น สาเหตุที่เป็นไปได้เพียงอย่างเดียวที่ฉันเห็นคือ:

# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done

# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command

# an alias for `true' (lazy programming)
while : ; do command ; done

ฉันเดาว่าสิ่งที่ฉันกำลังมองหาคือแอปพลิเคชั่นทางประวัติศาสตร์ที่อาจมี



69
@Caleb - ฉันถามเมื่อสองปีก่อนหน้านั้น
แอมเฟตามาจิน

ฉันจะไม่พูดคำสั่งที่ส่งคืนค่าเฉพาะ "ไม่ทำอะไรเลย" เว้นแต่การตั้งโปรแกรมการทำงานที่ประกอบด้วย "ไม่ทำอะไรเลย" :-)
LarsH

คำตอบ:


415

ในอดีตกระสุน Bourne ไม่ได้มีtrueและfalseเป็นคำสั่งในตัว trueแทนที่จะเป็นเพียงนามแฝง:และเพื่อสิ่งที่ต้องการfalselet 0

:ดีกว่าความtrueสามารถในการพกพาไปหากระสุนโบราณที่ได้จาก Bourne ตัวอย่างง่ายๆให้พิจารณาว่าไม่มีตัว!ดำเนินการไพพ์ไลน์หรือตัวดำเนินการ||รายการ (เช่นกรณีของเชลล์บอร์นโบราณบางตัว) สิ่งนี้ทำให้ส่วนelseคำifสั่งเป็นเพียงวิธีเดียวสำหรับการแยกสาขาตามสถานะออก:

if command; then :; else ...; fi

เนื่องจากifต้องใช้ส่วนthenคำสั่งที่ไม่ว่างเปล่าและความคิดเห็นไม่นับเป็นแบบไม่ว่างจึง:ทำหน้าที่เป็นแบบไม่ใช้งาน

ปัจจุบัน (นั่นคือ: ในบริบทที่ทันสมัย) คุณสามารถมักจะใช้อย่างใดอย่างหนึ่งหรือ: trueทั้งสองถูกระบุโดย POSIX และบางคนtrueอ่านง่ายขึ้น แต่มีหนึ่งความแตกต่างที่น่าสนใจ: :เป็นสิ่งที่เรียกว่า POSIX พิเศษในตัวในขณะที่trueเป็นปกติในตัว

  • บิวด์อินพิเศษต้องถูกสร้างไว้ในเชลล์ บิวด์อินปกติทั่วไปเป็นบิวท์อินปกติเท่านั้น แต่ไม่ได้รับประกันอย่างเข้มงวด มักจะไม่ควรเป็นโปรแกรมปกติที่:มีชื่อฟังก์ชั่นtrueใน PATH ของระบบส่วนใหญ่

  • อาจมีความแตกต่างที่สำคัญที่สุดคือการมีบิวด์อินพิเศษตัวแปรใด ๆ ที่กำหนดโดยบิวด์อิน - แม้ในสภาพแวดล้อมระหว่างการประเมินคำสั่งอย่างง่าย - ยังคงมีอยู่หลังจากคำสั่งเสร็จสมบูรณ์ดังแสดงโดยใช้ ksh93:

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $

    โปรดทราบว่า Zsh จะข้ามข้อกำหนดนี้ไปเช่นเดียวกับ GNU Bash ยกเว้นเมื่อทำงานในโหมดความเข้ากันได้ของ POSIX แต่เชลล์ "POSIX sh ที่ได้รับ" หลักอื่น ๆ ทั้งหมดจะสังเกตเห็นสิ่งนี้รวมถึง dash, ksh93 และ mksh

  • ความแตกต่างอีกประการหนึ่งคือบิวด์อินปกติต้องสามารถใช้งานร่วมกับexec- แสดงที่นี่โดยใช้ Bash:

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
  • POSIX ยังบันทึกอย่างชัดเจนว่า:อาจเร็วกว่าtrueแม้ว่านี่จะเป็นรายละเอียดเฉพาะของการติดตั้งใช้งาน


คุณหมายถึงบิวด์อินปกติไม่สามารถใช้งานร่วมกันได้execหรือไม่
โปรเก่า

1
@OldPro: ไม่เขาถูกต้องในtrueเรื่องนี้เป็น builtin ปกติ แต่เขาไม่ถูกต้องที่execใช้/bin/trueแทน builtin
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
@DennisWilliamson ฉันเพิ่งจะไปตามวิธีที่ข้อมูลจำเพาะถูกใช้ ความหมายนั้นแน่นอนว่าตัวบิวอินปกติควรมีเวอร์ชั่นสแตนด์อโลนอยู่ด้วย
ormaaj

17
+1 คำตอบที่ยอดเยี่ยม ฉันยังต้องการที่จะบันทึกการใช้งานสำหรับการเริ่มต้นตัวแปรเช่น: ${var?not initialized}et al
tripleee

การติดตามผลที่ไม่เกี่ยวข้องมากหรือน้อย คุณบอกว่า:เป็นตัวในตัวและไม่ควรมีฟังก์ชั่นตั้งชื่อโดย แต่ไม่ใช่ตัวอย่างที่เห็นได้บ่อยที่สุดของ fork bomb ที่:(){ :|: & };:ตั้งชื่อฟังก์ชั่นด้วยชื่อ:?
ปากช่อง

63

ฉันใช้เพื่อเปิด / ปิดคำสั่งตัวแปรอย่างง่ายดาย:

#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
    vecho=":"     # no "verbose echo"
else
    vecho=echo    # enable "verbose echo"
fi

$vecho "Verbose echo is ON"

ดังนั้น

$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON

สิ่งนี้ทำให้สคริปต์สะอาด สิ่งนี้ไม่สามารถทำได้ด้วย '#'

นอกจากนี้

: >afile

เป็นหนึ่งในวิธีที่ง่ายที่สุดในการรับประกันว่ามี 'afile' อยู่ แต่มีความยาวเป็น 0


20
>afileยิ่งง่ายขึ้นและได้ผลเหมือนเดิม
เอิร์ล

2
เยี่ยมมากฉันจะใช้เคล็ดลับ $ vecho เพื่อทำให้สคริปต์ที่ฉันบำรุงรักษาง่ายขึ้น
BarneySchmale

5
ประโยชน์ที่ได้รับจากการอ้างถึงลำไส้ใหญ่vecho=":"คืออะไร? เพียงอ่านง่าย
leoj

56

แอปพลิเคชันที่มีประโยชน์สำหรับ: คือถ้าคุณสนใจที่จะใช้การขยายพารามิเตอร์สำหรับผลข้างเคียงแทนที่จะส่งผ่านผลลัพธ์ไปยังคำสั่งจริง ในกรณีที่คุณใช้ PE เป็นอาร์กิวเมนต์ทั้งนี้หรือเท็จขึ้นอยู่กับว่าคุณต้องการออกจากสถานะของ 0 หรือ 1 : "${var:=$1}"ตัวอย่างเช่นอาจจะเป็น เนื่องจาก:เป็นแบบในตัวมันควรจะค่อนข้างเร็ว


2
นอกจากนี้คุณยังสามารถใช้เพื่อผลข้างเคียงของการขยายตัวทางคณิตศาสตร์: : $((a += 1))( ++และ--ผู้ประกอบการไม่จำเป็นต้องดำเนินการตาม POSIX) ใน bash, ksh และเชลล์อื่น ๆ ที่เป็นไปได้ที่คุณสามารถทำได้: ((a += 1))หรือ((a++))แต่ไม่ได้ระบุโดย POSIX
pabouk

@pabouk ใช่นั่นเป็นความจริงทุกอย่างแม้ว่าจะ(())ถูกระบุว่าเป็นคุณสมบัติเสริม "ถ้าลำดับอักขระเริ่มต้นด้วย" (("จะแยกวิเคราะห์โดยเชลล์เป็นส่วนขยายเลขคณิตถ้านำหน้าด้วย '$', เชลล์ที่ใช้ส่วนขยายโดยที่" ((นิพจน์))) "จะถูกประเมินเป็นนิพจน์ทางคณิตศาสตร์ "((" เป็นการแนะนำเป็นการประเมินทางคณิตศาสตร์แทนที่จะเป็นคำสั่งการจัดกลุ่ม "
ormaaj

50

:ยังสามารถใช้สำหรับความคิดเห็นบล็อก (คล้ายกับ / * * / ในภาษา C) ตัวอย่างเช่นหากคุณต้องการข้ามบล็อกรหัสในสคริปต์ของคุณคุณสามารถทำได้:

: << 'SKIP'

your code block here

SKIP

3
ความคิดที่ไม่ดี การทดแทนคำสั่งใด ๆ ภายในเอกสารที่นี่ยังคงดำเนินการอยู่
chepner

33
ไม่ใช่ความคิดที่เลว คุณสามารถหลีกเลี่ยงการแก้ไขตัวแปร / การทดแทนเอกสารที่นี่ได้โดยการอ้างถึงตัวคั่น: << 'SKIP'
Rondo

1
IIRC คุณยังสามารถ\ หลบหนีใด ๆ : <<\SKIPของตัวละครตัวคั่นสำหรับผลเดียวกัน:
yyny

@zagpoint นี่คือสิ่งที่ Python ใช้ docstrings เป็นความคิดเห็นหลายบรรทัดหรือไม่
Sapphire_Brick

31

หากคุณต้องการตัดไฟล์เป็นศูนย์ไบต์มีประโยชน์สำหรับการล้างบันทึกลองสิ่งนี้:

:> file.log

16
> file.logง่ายขึ้นและบรรลุผลเดียวกัน
แอมเฟตามา

55
แต่ใบหน้าที่มีความสุขคือสิ่งที่ทำเพื่อฉัน:>
Ahi Tuna

23
@amphetamachine: :>พกพาได้มากกว่า เชลล์บางตัว (เช่นของฉันzsh) สร้างอินสแตนซ์ของแมวในเชลล์ปัจจุบันโดยอัตโนมัติและฟัง stdin เมื่อได้รับการเปลี่ยนเส้นทางโดยไม่มีคำสั่ง แทนที่จะcat /dev/null, :ง่ายมาก บ่อยครั้งที่พฤติกรรมนี้แตกต่างกันในเชลล์แบบโต้ตอบแทนที่จะเป็นสคริปต์ แต่ถ้าคุณเขียนสคริปต์ในลักษณะที่ทำงานแบบโต้ตอบการดีบักโดยการคัดลอกวางก็ง่ายกว่ามาก
Caleb

2
วิธีการที่ไม่: > fileแตกต่างจากtrue > file(นอกเหนือจากการนับตัวอักษรและใบหน้ามีความสุข) ในเปลือกที่ทันสมัย (สมมติ:และtrueเท่าเทียมกันอย่างรวดเร็ว)?
Adam Katz


29

การใช้งานมากกว่าสองรายการไม่ได้กล่าวถึงในคำตอบอื่น

เข้าสู่ระบบ

ใช้สคริปต์ตัวอย่างนี้:

set -x
: Logging message here
example_command

บรรทัดแรกset -xทำให้เชลล์พิมพ์คำสั่งก่อนเรียกใช้ มันค่อนข้างเป็นโครงสร้างที่มีประโยชน์ ข้อเสียคือecho Log messageตอนนี้คำสั่งประเภทปกติจะพิมพ์ข้อความสองครั้ง วิธีการลำไส้ใหญ่ได้รับรอบที่ echoโปรดทราบว่าคุณจะยังคงต้องหลบหนีตัวอักษรพิเศษเช่นเดียวกับที่คุณต้องการสำหรับ

ตำแหน่งงาน Cron

ฉันเคยเห็นมันถูกใช้ในงาน cron เช่นนี้:

45 10 * * * : Backup for database ; /opt/backup.sh

นี่เป็นงาน cron ที่รันสคริปต์/opt/backup.shทุกวันเวลา 10:45 น. ข้อได้เปรียบของเทคนิคนี้คือมันช่วยให้วิชาอีเมลที่ดูดีขึ้นเมื่อ/opt/backup.shพิมพ์ออกมา


ตำแหน่งบันทึกเริ่มต้นอยู่ที่ไหน ฉันสามารถตั้งค่าตำแหน่งบันทึกได้หรือไม่ วัตถุประสงค์เพิ่มเติมสำหรับการสร้างเอาต์พุตใน stdout ระหว่างกระบวนการสคริปต์ / พื้นหลังหรือไม่?
domdambrogia

1
@domdambrogia เมื่อใช้set -xคำสั่งที่พิมพ์ออกมา (รวมถึงสิ่งที่ชอบ: foobar) ไปที่ stderr
Flimm

23

คุณสามารถใช้มันร่วมกับ backticks ( ``) เพื่อรันคำสั่งโดยไม่แสดงเอาต์พุตเช่นนี้:

: `some_command`

แน่นอนคุณสามารถทำได้some_command > /dev/nullแต่:รุ่นที่ค่อนข้างสั้นกว่า

ที่ถูกกล่าวว่าฉันจะไม่แนะนำให้ทำเช่นนั้นเพราะมันจะทำให้คนสับสน มันมาถึงใจเป็นกรณีการใช้งานที่เป็นไปได้


25
สิ่งนี้ไม่ปลอดภัยหากคำสั่งจะถ่ายโอนข้อมูลเอาต์พุตจำนวนไม่กี่เมกะไบต์เนื่องจากเชลล์บัฟเฟอร์ส่งออกข้อมูลแล้วส่งผ่านเป็นอาร์กิวเมนต์บรรทัดคำสั่ง (พื้นที่สแต็ค) ถึง ':'
Juliano

15

นอกจากนี้ยังมีประโยชน์สำหรับโปรแกรมหลายภาษา:

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

ตอนนี้เป็นทั้งปฏิบัติการเปลือกสคริปต์และโปรแกรม JavaScript: ความหมาย./filename.js, sh filename.jsและnode filename.jsการทำงานทั้งหมด

(เป็นการใช้งานที่แปลกนิดหน่อย แต่ก็มีประสิทธิภาพ)


คำอธิบายบางอย่างตามที่ร้องขอ:

  • เชลล์สคริปต์จะถูกประเมินทีละบรรทัด; และexecคำสั่งเมื่อรันจะยกเลิกเชลล์และแทนที่ด้วยกระบวนการด้วยคำสั่ง resultant ซึ่งหมายความว่าสำหรับเชลล์โปรแกรมจะมีลักษณะดังนี้:

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
  • ตราบใดที่ไม่มีการขยายพารามิเตอร์หรือนามแฝงเกิดขึ้นในคำคำใด ๆในเชลล์สคริปต์สามารถห่อด้วยเครื่องหมายคำพูดโดยไม่เปลี่ยนความหมาย ซึ่งหมายความว่า':'เทียบเท่ากับ:(เราใส่เฉพาะในเครื่องหมายคำพูดที่นี่เพื่อให้บรรลุความหมาย JavaScript ที่อธิบายด้านล่าง)

  • ... และตามที่อธิบายไว้ข้างต้นคำสั่งแรกในบรรทัดแรกคือ no-op (มันแปลเป็น: //หรือถ้าคุณต้องการที่จะอ้างคำ, ':' '//'สังเกตว่าการ//ดำเนินการไม่มีความหมายพิเศษที่นี่เช่นเดียวกับใน JavaScript; มันเป็นเพียงคำที่ไร้ความหมายที่ถูกโยนทิ้งไป)

  • ในที่สุดคำสั่งที่สองในบรรทัดแรก (หลังเซมิโคลอน) เป็นเนื้อจริงของโปรแกรม: มันเป็นexecสายที่แทนที่เชลล์สคริปต์ที่ถูกเรียกด้วยกระบวนการ Node.js เรียกเพื่อประเมินส่วนที่เหลือของสคริปต์

  • ในขณะที่บรรทัดแรกใน JavaScript แยกวิเคราะห์เป็นตัวอักษรสตริง ( ':') และจากนั้นความคิดเห็นซึ่งจะถูกลบ; ดังนั้นสำหรับ JavaScript โปรแกรมจะมีลักษณะดังนี้:

    ':'
    ~function(){ ... }

    เนื่องจาก string-literal อยู่บนบรรทัดด้วยตัวมันเองมันเป็นคำสั่ง no-op และถูกถอดออกจากโปรแกรม นั่นหมายความว่าจะลบทั้งบรรทัดทิ้งไว้เพียงโค้ดโปรแกรมของคุณ (ในตัวอย่างนี้function(){ ... }เนื้อความ)


สวัสดีคุณสามารถอธิบายสิ่งที่ต้องทำ: //;และ~function(){}ทำอย่างไร ขอบคุณ:)
Stphane

1
@Stphane เพิ่มการแยกย่อย! ส่วนเรื่อง~function(){}นั้นซับซ้อนกว่าเล็กน้อย มีคู่คำตอบอื่น ๆ ที่นี่ที่สัมผัสกับมันแม้จะไม่มีของพวกเขาจริงๆอธิบายให้ความพึงพอใจของฉัน ... ถ้าค่าของคำถามเหล่านั้นอธิบายได้ดีพอสำหรับคุณรู้สึกอิสระที่จะโพสต์มันเป็นคำถามที่นี่ฉันจะเป็น ยินดีที่จะตอบเชิงลึกในคำถามใหม่
ELLIOTTCABLE

1
nodeผมไม่ได้ให้ความสนใจกับ ดังนั้นส่วนของฟังก์ชั่นนั้นเกี่ยวกับ javascript! ฉันโอเคกับโอเปอเรเตอร์ต่อหน้า IIFE ฉันคิดว่านี่เป็นการทุบตีในตอนแรกและจริง ๆ แล้วไม่ได้รับความหมายของโพสต์ของคุณ ฉันโอเคขอบคุณสำหรับเวลาที่ใช้ในการเพิ่ม«การทำลาย "!
Stphane

~{ No problem. (= }
ELLIOTTCABLE

12

ฟังก์ชั่นการจัดทำเอกสารด้วยตนเอง

คุณยังสามารถใช้:เพื่อฝังเอกสารในฟังก์ชั่น

สมมติว่าคุณมีสคริปต์ไลบรารีที่mylib.shมีฟังก์ชั่นที่หลากหลาย คุณสามารถเลือกแหล่งที่มาของไลบรารี ( . mylib.sh) และเรียกใช้ฟังก์ชันได้โดยตรงหลังจากนั้น ( lib_function1 arg1 arg2) หรือหลีกเลี่ยงความยุ่งเหยิงในเนมสเปซของคุณและเรียกใช้ไลบรารีด้วยอาร์กิวเมนต์ของฟังก์ชัน ( mylib.sh lib_function1 arg1 arg2)

มันจะไม่ดีถ้าคุณสามารถพิมพ์mylib.sh --helpและรับรายการฟังก์ชั่นที่มีอยู่และการใช้งานของพวกเขาโดยไม่ต้องบำรุงรักษารายการฟังก์ชั่นในข้อความช่วยเหลือด้วยตนเอง?

#! / bin / ทุบตี

# ฟังก์ชัน "สาธารณะ" ทั้งหมดต้องเริ่มต้นด้วยส่วนนำหน้านี้
LIB_PREFIX = 'lib_'

# "ฟังก์ชั่นห้องสมุด"
lib_function1 () {
    : ฟังก์ชั่นนี้ทำอะไรบางอย่างที่ซับซ้อนด้วยสองข้อโต้แย้ง
    :
    : พารามิเตอร์:
    : 'arg1 - อาร์กิวเมนต์แรก ($ 1)'
    : 'arg2 - อาร์กิวเมนต์ที่สอง'
    :
    : ผลลัพธ์:
    : " มันซับซ้อน"

    # รหัสฟังก์ชันจริงเริ่มต้นที่นี่
}

lib_function2 () {
    : เอกสารประกอบการใช้งาน

    # รหัสฟังก์ชั่นที่นี่
}

ฟังก์ชั่นความช่วยเหลือ #
--ช่วยด้วย() {
    echo MyLib v0.0.1
    เสียงสะท้อน
    การใช้งาน echo: mylib.sh [function_name [args]]
    เสียงสะท้อน
    echo ฟังก์ชั่นที่มีอยู่:
    ประกาศ -f | sed -n -e '/ ^' $ LIB_PREFIX '/, / ^} $ / {/ \ (^' $ LIB_PREFIX '\) \ | \ (^ [\ t] *: \) / {
        s / ^ \ ('$ LIB_PREFIX'. * \) () / \ n === \ 1 === /; s / ^ [\ t] *: \? ['\' '"] \? / / ; s / [ '\' '"] \; \ $ //; p}}'
}

# รหัสหลัก
if ["$ {BASH_SOURCE [0]}" = "$ {0}"]; แล้วก็
    # สคริปต์ถูกดำเนินการแทนที่มา
    # เรียกใช้ฟังก์ชันที่ร้องขอหรือแสดงความช่วยเหลือ
    if ["$ (type -t -" $ 1 "2> / dev / null)" = function]; แล้วก็
        "$ @"
    อื่น
        --ช่วยด้วย
    Fi
Fi

ความคิดเห็นเล็กน้อยเกี่ยวกับรหัส:

  1. ฟังก์ชัน "สาธารณะ" ทั้งหมดมีคำนำหน้าเหมือนกัน มีเพียงผู้ใช้เท่านั้นที่ถูกเรียกใช้และแสดงอยู่ในข้อความช่วยเหลือ
  2. คุณสมบัติการจัดทำเอกสารด้วยตนเองขึ้นอยู่กับจุดก่อนหน้าและใช้declare -fในการระบุฟังก์ชั่นที่มีอยู่ทั้งหมดจากนั้นกรองผ่าน Sed เพื่อแสดงเฉพาะฟังก์ชั่นที่มีส่วนนำหน้าที่เหมาะสม
  3. เป็นความคิดที่ดีที่จะใส่เอกสารไว้ในเครื่องหมายคำพูดเดี่ยวเพื่อป้องกันการขยายที่ไม่ต้องการและการลบช่องว่าง คุณจะต้องระมัดระวังเมื่อใช้เครื่องหมายอัญประกาศเดี่ยว / คำพูดในข้อความ
  4. คุณสามารถเขียนโค้ดเพื่อ internalize คำนำหน้าห้องสมุดคือผู้ใช้เท่านั้นที่มีการพิมพ์และจะได้รับการแปลภายในเพื่อmylib.sh function1 lib_function1นี่คือแบบฝึกหัดที่เหลือไว้สำหรับผู้อ่าน
  5. ฟังก์ชั่นความช่วยเหลือมีชื่อว่า "--help" นี้เป็นความสะดวกสบาย (เช่นขี้เกียจ) $1วิธีการที่ใช้ห้องสมุดวิงวอนกลไกในการแสดงความช่วยเหลือตัวเองได้โดยไม่ต้องรหัสการตรวจสอบพิเศษสำหรับ ในเวลาเดียวกันมันจะถ่วงชื่อของคุณถ้าคุณแหล่งที่มาของห้องสมุด หากคุณไม่ชอบคุณสามารถเปลี่ยนชื่อเป็นชื่อที่ต้องการlib_helpหรือตรวจสอบ args สำหรับ--helpรหัสหลักและเรียกใช้ฟังก์ชันช่วยเหลือด้วยตนเอง

4

ฉันเห็นการใช้งานนี้ในสคริปต์และคิดว่ามันเป็นสิ่งที่ดีสำหรับการเรียกใช้ basename ภายในสคริปต์

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

... นี่เป็นการแทนที่รหัส: basetool=$(basename $0)


ฉันชอบbasetool=${0##*/}
Amit Naidu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.