$ {1 +“ $ @”} หมายถึงอะไรในเชลล์สคริปต์และมันแตกต่างจาก“ $ @” อย่างไร


43

ในเอกสารประกอบ Perl, perlrun (1)แนะนำการเรียกใช้สคริปต์ Perl โดยใช้ส่วนหัวของสองภาษาเชลล์ / Perl:

#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
    if 0;

อะไร${1+"$@"}หมายถึง? ฉันลองใช้"$@"แทน (โดยใช้ Bash เป็น / bin / sh) และดูเหมือนว่าจะใช้งานได้เช่นกัน


แก้ไข

${1:+"$@"}สองคำตอบด้านล่างบอกว่ามันควรจะเป็น ฉันรับรู้ถึง${parameter:+word}ไวยากรณ์ ("ใช้ค่าทางเลือก") ที่ระบุไว้ใน bash (1) อย่างไรก็ตามฉันไม่มั่นใจเพราะ

  1. ทั้งสองอย่าง${1+"$@"}และใช้"$@"งานได้ดีแม้ว่าจะไม่มีพารามิเตอร์ก็ตาม ถ้าฉันสร้าง simple.sh เป็น

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 "$@"'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);

    และ question.sh เป็น

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 ${1+"$@"}'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);

    ฉันสามารถทำให้ทั้งคู่ทำงานเหมือนกัน:

    $ ./question.sh 
    $VAR1 = [];
    $ ./question.sh a
    $VAR1 = [
              'a'
            ];
    $ ./question.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./question.sh ""
    $VAR1 = [
              ''
            ];
    $ ./simple.sh 
    $VAR1 = [];
    $ ./simple.sh a
    $VAR1 = [
              'a'
            ];
    $ ./simple.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./simple.sh ""
    $VAR1 = [
              ''
            ];
  2. แหล่งข้อมูลอื่นบนอินเทอร์เน็ตยังใช้${1+"$@"}รวมถึงแฮ็กเกอร์หนึ่งคนที่ดูเหมือนจะรู้ว่าเขากำลังทำอะไรอยู่

อาจ${parameter+word}จะเป็นไวยากรณ์ทางเลือก (หรือเลิกใช้) ทางเลือกที่ไม่มีเอกสาร${parameter:+word}หรือ มีคนยืนยันสมมติฐานนั้นได้ไหม


ดูเหมือนgrawlixes
Martin Thoma

คำตอบ:


53

นั่นเป็นความเข้ากันได้กับเชลล์เป้าหมาย The Bourne shell เป็นเชลล์ตัวเก่าที่เปิดตัวครั้งแรกกับ Unix เวอร์ชั่น 7 ในปี 1979 และยังคงพบได้ทั่วไปจนถึงกลางปี ​​90 /bin/shใน Unices เชิงพาณิชย์ส่วนใหญ่

มันเป็นบรรพบุรุษของที่สุดเปลือกหอยบอร์นเหมือนเช่นksh, หรือbashzsh

มันมีคุณสมบัติบางอย่างที่น่าอึดอัดใจหลายอย่างได้รับการแก้ไขkshและเชลล์อื่น ๆ และสเปคมาตรฐานใหม่ของshหนึ่งในนั้นคือ:

ด้วยเชลล์เป้าหมาย (อย่างน้อยตัวแปรเหล่านั้นซึ่งยังไม่ได้รับการแก้ไข): "$@"ขยายอาร์กิวเมนต์ว่างหนึ่งรายการหากรายการพารามิเตอร์ตำแหน่งว่างเปล่า ( $# == 0) แทนที่จะไม่มีอาร์กิวเมนต์เลย

${var+something}ขยายเป็น "บางอย่าง" เว้นแต่$varจะไม่มีการตั้งค่า มันมีการบันทึกไว้อย่างชัดเจนในเชลล์ทั้งหมด แต่หาได้ยากในbashเอกสารตามที่คุณต้องใส่ใจกับประโยคนี้:

เมื่อไม่ทำการขยายสายอักขระย่อยโดยใช้แบบฟอร์มที่มีเอกสารด้านล่างทดสอบการทุบตีสำหรับพารามิเตอร์ที่ไม่ได้ตั้งค่าหรือเป็นโมฆะ ไม่ใส่ผลลำไส้ใหญ่ในการทดสอบเท่านั้นสำหรับพารามิเตอร์ที่เป็น unset

ดังนั้น${1+"$@"}ขยายเป็น"$@"ถ้า$1มีการตั้งค่า ( $# > 0) ซึ่งแก้ไขข้อ จำกัด ของเชลล์เป้าหมายได้

โปรดทราบว่าเชลล์เป้าหมายเป็นเชลล์เดียวที่มีปัญหานั้น Modern shs (ที่shสอดคล้องกับข้อกำหนด POSIX ของsh(ซึ่ง Bourne shell ไม่ใช่)) ไม่มีปัญหานั้น ดังนั้นคุณต้องการเพียงว่าถ้าคุณต้องการรหัสของคุณเพื่อทำงานบนระบบเก่ามากซึ่ง/bin/shอาจเป็น Bourne shell แทนเชลล์มาตรฐาน (โปรดทราบว่า POSIX ไม่ได้ระบุตำแหน่งของมาตรฐานshดังนั้นตัวอย่างเช่นบน Solaris ก่อน Solaris 11 /bin/shยังคงเป็นเชลล์เป้าหมาย (แม้ว่าไม่มีปัญหานั้น) ในขณะที่มาตรฐาน / ปกติshอยู่ในตำแหน่งอื่น ( /usr/xpg4/bin/sh)

มีปัญหาในperlrunหน้า perldoc นั้นในที่$0ไม่ได้ยกมาแม้ว่า

ดูhttp://www.in-ulm.de/~mascheck/various/bourne_args/สำหรับข้อมูลเพิ่มเติม


ไม่สามารถทำซ้ำได้ใน Heirloom อาจไม่สามารถใช้กับ Solaris ได้
ormaaj

2
@ormaaj ใช่เห็นหน้าที่ฉันเชื่อมโยง ได้รับการแก้ไขใน SVR3 และ Solaris / SunOS สูงกว่า 5 รีบูตบน SVR4 และหน้านั้นระบุว่า 4.1.x ไม่มีปัญหาเช่นกัน
Stéphane Chazelas

ฉันเข้าใจแล้ว <3 หน้า Mascheck
ormaaj

3

มีความแตกต่างระหว่าง:

command ""

และ

command

ในหนึ่งคุณจะผ่านหนึ่งอาร์กิวเมนต์ที่เป็นสตริงที่ว่างเปล่า ในครั้งที่สองไม่มีการโต้แย้งใด ๆ เลย

สำหรับทั้ง "$ @" ""จะถือเอาสิ่งเดียวกัน แต่การใช้${1:+"$@"}จะเป็น""ครั้งแรกและไม่มีข้อโต้แย้งสำหรับวินาทีซึ่งเป็นเจตนา

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

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"

สิ่งนี้จะพยายามเรียกใช้ "" บนรีโมตโฮสต์ (ซึ่งเพิ่งกลับมา) มันจะไม่เป็นเชลล์แบบโต้ตอบ

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}

จะเริ่มเชลล์เชิงโต้ตอบบนโฮสต์ระยะไกลเพราะ exec จะตีความตัวแปรอย่างถูกต้องว่าไม่มีอะไรไม่ใช่สตริงว่างเปล่า

อ่านเพิ่มเติมเกี่ยวกับการใช้งาน "$ {parameter: + word}" ("Use Alternate Value") และสตริงการขยายตัวของตัวแปรที่คล้ายกันในหน้า bash manual


สิ่งนี้ดูเหมือนว่าจะนำไปใช้กับเชลล์เป้าหมายเท่านั้นและไม่ใช่สำหรับshการติดตั้งตามมาตรฐาน POSIX (ดูคำตอบอื่น ๆ )
törzsmókus

4
ไม่${1:+"$@"}จะขยายเป็นไม่มีอาร์กิวเมนต์หาก$1ว่างเปล่า ${1+"$@"}คุณต้องการ โดยทั่วไปคุณมีการกลับรายการ
Stéphane Chazelas

0

ฉันสรุปคำตอบของStéphane Chazelas:

  • $ {1: + "$ @"} 'ทดสอบว่า $ 1 เป็นโมฆะหรือไม่มีการตั้งค่า
  • $ {1 + "$ @"} 'ทดสอบว่า $ 1 unset หรือไม่

ดังนั้นหากใช้อันที่สองพร้อมพารามิเตอร์ "" หมายความว่า $ 1 เป็นโมฆะ แต่ไม่ได้ทดสอบว่ามันเป็นโมฆะหรือไม่มันเพิ่งเห็นว่ามันได้ถูกตั้งค่าไว้แล้วอย่างไรก็ตามมันว่างเปล่าหรือไม่ดังนั้นมันจะขยาย $ @ แต่คุณใช้ $ {1: + "$ @"} 'กับ "" มันจะไม่ขยาย$@อีกต่อไป


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