在複合命令中設定 bash 選項

在複合命令中設定 bash 選項

我發現extglob僅在複合複合中設定 shell 選項會導致後續擴展 glob 失敗。是否需要在複合命令之外設定 shell 選項?我在 Bash 手冊頁中沒有看到這樣的要求。

例如,以下腳本可以正常工作(列印a.0 a.1):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
shopt -s extglob
ls "a."!(b*)

但是,如果最後兩行作為複合命令執行,則腳本將失敗並出現以下錯誤:

syntax error near unexpected token `('
`    ls "a."!(b*)'

這是使用 4.2 到 4.4 的 Bash 版本以及各種複合命令:

(1) 有條件的-if

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
if true; then
    shopt -s extglob
    ls "a."!(b*)
fi

(2) 大括號—{ }

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
{
    shopt -s extglob
    ls "a."!(b*)
}

(3) 子外殼 -- ( ):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
(
    shopt -s extglob
    ls "a."!(b*)
)

在所有情況下,如果shopt將 移到複合命令之外,腳本就會成功。

答案1

在解析整個命令之前,複合命令的任何部分都不會執行,這間接記錄在外殼操作手冊的部分:所有標記化和解析都發生在「the」指令執行之前。啟用extglob透過新增新的模式比對運算子來變更語言語法,這些運算子在標記化過程中會被識別。

因為到達shopt時該命令尚未執行,所以它被視為嘗試歷史擴展 ( ) 或控制運算符,而不是被視為單個項目(與等相同,只是沒有歷史擴展那裡)。!(!(!(@(

當解析到達該標記時,任何一種情況通常都會出錯,儘管!(...)在行的開頭或之後time是否定的子 shell 管道。 「unexpected token `('」表示它無法在該位置接受子 shell 表達式。

當您只想在子 shell 中暫時啟用 extglob 時,這有點煩人。一種解決方法是定義一個使用所需 glob 的函數,然後重新停用 extglob,然後然後在啟用 extglob 的情況下從子 shell 呼叫該函數:

shopt -s extglob
f() { ls "a."!(b*) ; }
shopt -u extglob
(
    shopt -s extglob
    f
)

它非常笨重。


如果您在複合命令中建立別名,則會發生相同的效果,因為它們也會在解析之前進行處理:

if true
then
    alias foo=echo
    foo bar
fi
foo xyz

將列印foo: command not found, 然後xyz, 因為別名if創建,但在完成之前不可用。

相關內容