BATsh สรุปย่อ [TH] ภาษาไทย
==========================

----------------------------------------------------------------------
บทสรุป
  BATsh เป็นเชลล์สองภาษาที่รันไวยากรณ์แบตช์ cmd.exe และ bash/sh
  ในไฟล์สคริปต์เดียวกัน โดยสลับโหมดอัตโนมัติเป็นรายบรรทัด/รายส่วน
  ไม่ต้องใช้เชลล์ภายนอก -- เป็นการพัฒนาด้วย Perl ล้วน
  รองรับไปป์ไลน์ การเปลี่ยนทิศทาง ฟังก์ชัน และส่วนขยายการแทนค่าตัวแปร

ตัวอย่างโหมดผสม
  :: ส่วน CMD (โทเคนแรกเป็นตัวพิมพ์ใหญ่)
  @ECHO OFF
  SET LANG=BATsh
  SET COUNT=3

  # ส่วน SH (โทเคนแรกเป็นตัวพิมพ์เล็ก)
  greet() { echo "Hello from $1 (bash mode)"; }
  greet $LANG
  for i in 1 2 3; do echo "  item $i of $COUNT"; done
  result=$(echo $LANG | perl -e 'while(<STDIN>){chomp;print uc}')
  echo "Uppercase: $result"

  :: เข้าส่วน CMD อีกครั้ง (อ่านผลลัพธ์ของ SH ผ่านบริดจ์)
  ECHO Back in CMD: %result%

  # รันด้วย: perl lib/BATsh.pm script.batsh
  # หรือ:    use BATsh; BATsh->run('script.batsh');
----------------------------------------------------------------------


BATsh เป็นเชลล์สองภาษาที่รันไวยากรณ์ cmd.exe และ bash/sh ในสคริปต์เดียวกัน
สคริปต์จะถูกแบ่งออกเป็นส่วน ๆ แต่ละส่วนจะถูกรันตามต้นฉบับโดยเชลล์ที่เหมาะสม

1. การตรวจหาโหมด
----------------
  โทเคนแรกของบรรทัดที่มีเนื้อหาบรรทัดแรกของส่วนหนึ่ง ๆ จะกำหนดว่าเชลล์ใด
  จะรันส่วนนั้น:

  โหมด CMD: โทเคนแรกประกอบด้วย [A-Z 0-9 _ - \ / : . @ %] ทั้งหมด
            และมีอักษรตัวพิมพ์ใหญ่ A-Z อย่างน้อยหนึ่งตัว

    ECHO hello          -> ส่วน CMD (cmd.exe)
    SET FOO=bar baz     -> ส่วน CMD  (ไม่ตรวจส่วนของค่า)
    @ECHO OFF           -> ส่วน CMD
    IF "%X%"=="Y" (     -> ส่วน CMD

  โหมด SH: กรณีอื่น ๆ ทั้งหมด (มีตัวพิมพ์เล็ก หรือไม่มีตัวอักษรเลย)

    echo hello          -> ส่วน SH  (bash/sh)
    export FOO=bar      -> ส่วน SH
    if [ -f "$f" ]; then  -> ส่วน SH
    #!/bin/sh           -> ส่วน SH  (shebang เป็นบรรทัด SH)

  ความคิดเห็นและบรรทัดว่างจะถูกรวมเข้ากับส่วนปัจจุบัน
  ไวยากรณ์ความคิดเห็น:
    ::           ความคิดเห็นแบบ CMD
    REM ...      ความคิดเห็นแบบ CMD (ไม่แยกตัวพิมพ์ใหญ่เล็ก)
    @REM ...     ความคิดเห็นแบบ CMD
    # ...        ความคิดเห็นแบบ SH  (ไม่ใช่ shebang #!)

2. การเริ่มเชลล์
----------------
  perl lib/BATsh.pm               # REPL แบบโต้ตอบ
  perl lib/BATsh.pm script.batsh  # รันไฟล์สคริปต์
  perl lib/BATsh.pm -e "echo hi"  # คำสั่งบรรทัดเดียวแบบอินไลน์

  จาก Perl:
    use BATsh;
    BATsh->run('script.batsh');
    BATsh->run_string("echo hello");
    BATsh->repl();

3. บริดจ์ตัวแปรสภาพแวดล้อม
--------------------------
  ก่อนรันแต่ละส่วน BATsh จะแทรก %ENV ปัจจุบันเป็นส่วนนำ (บรรทัด SET สำหรับ
  CMD, บรรทัด export สำหรับ SH) หลังจากส่วนนั้น สภาพแวดล้อมสุดท้ายของเชลล์
  จะถูกอ่านกลับเข้าสู่ %ENV

  export FOO=hello   # SH ตั้งค่า FOO
  ECHO %FOO%         # CMD อ่าน FOO ผ่านบริดจ์ (Windows)

  SET BAR=world      # CMD ตั้งค่า BAR
  echo $BAR          # SH อ่าน BAR ผ่านบริดจ์

4. SETLOCAL / ENDLOCAL
----------------------
  SETLOCAL           # เก็บสแน็ปช็อต %ENV (จัดการโดย BATsh ไม่ใช่ cmd.exe)
  SET TMP=local_val
  ECHO %TMP%
  ENDLOCAL           # คืนค่า %ENV (TMP หายไป)

  ขอบเขตสามารถซ้อนกันได้

5. การตรวจหาขอบเขตส่วน
----------------------
  ส่วนหนึ่งจะสิ้นสุดเมื่อความลึกของบล็อกกลับเป็นศูนย์ และบรรทัดที่มีเนื้อหา
  ถัดไปอยู่ในโหมดที่ต่างออกไป

  ส่วน CMD ติดตามความลึกของ ( และ ) ที่อยู่นอกเครื่องหมายคำพูด:

    IF "%X%"=="Y" (     <- เปิดบล็อก (ความลึก 1)
        ECHO yes
    ) ELSE (            <- ปิดแล้วเปิดใหม่ (ความลึกคงไว้ >=1)
        ECHO no
    )                   <- ปิดบล็อก (ความลึก 0) -> ส่วนอาจสิ้นสุด

  ส่วน SH ติดตามความลึกของคีย์เวิร์ด:

    for x in 1 2; do   <- เปิดบล็อก (ความลึก 1)
        echo $x
    done                <- ปิดบล็อก (ความลึก 0) -> ส่วนอาจสิ้นสุด

  บรรทัดภายในบล็อกที่เปิดอยู่จะถูกรวมเข้ากับส่วนปัจจุบัน แม้โทเคนแรกของมัน
  จะดูเหมือนอีกโหมดหนึ่ง สิ่งนี้ทำให้:

    for x in A B; do
        ECHO $x          <- ตัวพิมพ์ใหญ่ภายในบล็อก SH: ยังเป็นส่วน SH
    done

  คู่คีย์เวิร์ด SH:
    ตัวเปิด (+1) : if  for  while  until  case  function  select  {
    ตัวปิด  (-1) : fi  done  esac  }
    เป็นกลาง ( 0) : then  do  else  elif

6. การกำหนดซับรูทีน
-------------------
  :GREET
  echo "Hello $BATSH_ARG1"
  RET

  เลเบลเริ่มต้นด้วย : และจบด้วย RET หรือ RETURN
  เนื้อหาจะถูกดึงออกก่อนการรัน (ไม่ได้รันแบบอินไลน์)
  เนื้อหาอาจมีบรรทัด CMD บรรทัด SH หรือทั้งสองผสมกัน

7. CALL และ source
------------------
  CALL :GREET world      # เรียกซับรูทีนพร้อมอาร์กิวเมนต์
  CALL other.batsh       # รวม/รันไฟล์ .batsh อื่น (CMD)
  source other.batsh     # รวม/รันไฟล์ .batsh อื่น (SH)
  . other.batsh          # สัญกรณ์จุดแบบ POSIX

  อาร์กิวเมนต์: $BATSH_ARG1 .. $BATSH_ARGn  (ใน CMD คือ %BATSH_ARG1%)
  จำนวน:        $BATSH_ARGC

8. Perl API
-----------
  BATsh->run($file)            # รันไฟล์ .batsh
  BATsh->run_string($source)   # รันสตริงต้นฉบับ
  BATsh->run_lines(@lines)     # รันอาร์เรย์ของบรรทัด
  BATsh->repl()                # REPL แบบโต้ตอบ
  BATsh->classify_token($tok)  # 'CMD' หรือ 'SH'
  BATsh->setlocal()            # สแน็ปช็อต %ENV
  BATsh->endlocal()            # คืนค่า %ENV
  BATsh->call_sub($lbl, @args) # เรียกซับรูทีน
  BATsh->source_file($file)    # รวมไฟล์ .batsh
  BATsh->version()             # สตริงเวอร์ชัน

9. หมายเหตุแพลตฟอร์ม
--------------------
  Windows: ทั้งส่วน CMD และ SH รันด้วย Perl ล้วน -- ไม่ต้องใช้ cmd.exe, bash หรือ sh ภายนอก
  UNIX:    ทั้งส่วน CMD และ SH รันด้วย Perl ล้วน -- ไม่ต้องใช้ cmd.exe, bash หรือ sh ภายนอก

10. ข้อกำหนด
------------
  Perl 5.005_03 หรือใหม่กว่า  ใช้เฉพาะโมดูลหลัก (File::Spec, Carp)
  ไม่มีการพึ่งพา CPAN

11. ไปป์ไลน์ CMD และตัวปรับแต่งพารามิเตอร์
------------------------------------------
  cmd1 | cmd2              # ไปป์ไลน์ผ่านไฟล์ชั่วคราว (Perl ล้วน)
  ECHO hello | perl -e "while(<STDIN>){print uc}"

  SET /P VAR=Prompt:       # อ่านหนึ่งบรรทัดจาก STDIN เข้าสู่ VAR

  ตัวปรับแต่งทิลเดอของพารามิเตอร์แบตช์ (เช่น เมื่อ %0=C:\scripts\deploy.bat):
    %~0   -> C:\scripts\deploy.bat  (เอาเครื่องหมายคำพูดออกเท่านั้น)
    %~f0  -> C:/scripts/deploy.bat  (เส้นทางสัมบูรณ์เต็ม)
    %~d0  -> C:                     (อักษรไดรฟ์)
    %~p0  -> /scripts/              (เส้นทางไดเรกทอรี)
    %~n0  -> deploy                 (ชื่อไฟล์ไม่มีนามสกุล)
    %~x0  -> .bat                   (นามสกุล)
    %~dp0 -> C:/scripts/            (ไดรฟ์ + ไดเรกทอรี ใช้บ่อยที่สุด)
    %~nx1 -> deploy.bat             (ชื่อ + นามสกุล)

12. ฟังก์ชัน SH และการแทนค่า
----------------------------
  greet() {              # การกำหนดฟังก์ชัน
      echo "Hi $1"
  }
  function add {         # ไวยากรณ์ทางเลือก
      echo $(( $1 + $2 ))
  }
  greet world            # เรียกฟังก์ชัน
  add 3 4                # -> 7

  ${var%.*}    ลบส่วนต่อท้ายที่สั้นที่สุดที่ตรงกับ .*
  ${var%%.*}   ลบส่วนต่อท้ายที่ยาวที่สุดที่ตรงกับ .*
  ${var#*.}    ลบส่วนนำหน้าที่สั้นที่สุดที่ตรงกับ *.
  ${var##*.}   ลบส่วนนำหน้าที่ยาวที่สุดที่ตรงกับ *.
  ${var/a/b}   แทนที่ a ครั้งแรกด้วย b
  ${var//a/b}  แทนที่ a ทุกครั้งด้วย b
  ${var^^}     เป็นตัวพิมพ์ใหญ่ทั้งหมด
  ${var,,}     เป็นตัวพิมพ์เล็กทั้งหมด
  ${var:2:4}   สตริงย่อยจากออฟเซ็ต 2 ความยาว 4
  ${#var}      ความยาวสตริง
  ${var:-def}  ค่าถ้าตั้งไว้ มิฉะนั้นใช้ def

13. การเปลี่ยนทิศทาง I/O ของ SH
-------------------------------
  cmd > file      เขียนทับ stdout
  cmd >> file     ต่อท้าย stdout
  cmd < file      stdin จากไฟล์
  cmd 2> file     stderr ไปยังไฟล์
  cmd 2>&1        รวม stderr เข้ากับ stdout
  cmd > f 2>&1    ทั้ง stdout และ stderr ไปยังไฟล์

  Here-document (อินพุตทาง stdin):
    cmd <<EOF       บรรทัดเนื้อหาจนถึง EOF; แทนค่า $VAR
    cmd <<'EOF'     บรรทัดเนื้อหาจนถึง EOF; ไม่แทนค่า (ตามตัวอักษร)
    cmd <<-EOF      เหมือน <<EOF แต่ตัดอักขระ TAB ที่ต้นบรรทัดออก

14. คำสั่งประกอบของ SH
----------------------
  cmd1 && cmd2    รัน cmd2 เฉพาะเมื่อ cmd1 สำเร็จ
  cmd1 || cmd2    รัน cmd2 เฉพาะเมื่อ cmd1 ล้มเหลว
  cmd1 ; cmd2     รัน cmd2 โดยไม่มีเงื่อนไข

ดูเพิ่มเติม: https://metacpan.org/dist/BATsh
