我正在閱讀幾本有關 bash 腳本編寫的書籍,並努力理解正確的引用和IFS
.也許有人可以幫我舉一個涉及引號的檔案名稱的小例子。從命令列執行此操作,可以正確列印檔案名,即使它們包含空格:
set - *
for i in "$@"; do echo $i; done
這不起作用,因為它會在空格處中斷:
set - `find . -name "*"`
for i in "$@"; do echo $i; done
也不:
IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done
IFS=$'\n'
使用和 的組合也不行-print
。為什麼這些都失敗了?
以下內容也會失敗,但在這種情況下會出錯(「bash:意外標記『do』附近的語法錯誤」)。為什麼?
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
但這是有效的(注意“;”):
IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done
這會失敗,因為檔案名稱根本沒有分割(for
僅循環一次):
IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done
那麼問題來了,為什麼第一個和第三個會失敗呢?
最後,我的信念是否正確,即設定時IFS
,''
與 相同$'\0'
? (我在前面的例子中嘗試了兩者。)如果是這樣,為什麼我顯然需要$'\n'
而不只是\n
?
*Ubuntu Gnome 16.04 中的 Bash 版本為 4.3.42(1)。
答案1
我在調試 bash 命令時學到了兩件事:一)如果它沒有像您想像的那樣工作,請 echo/printf 輸出命令以確保它們以您認為應該的方式顯示。二)手動執行命令中的命令以查看它們是否正常運行(除非這些命令具有潛在的破壞性,例如:rm、dd、chmod 777 等)。
您究竟想如何實現或實現什麼目標?我的似乎可以逐字使用該命令,因此您需要解釋您想要它做什麼。檔案名稱中是否有空格導致指令出現問題?只要您刪除 IFS 變數中的空格,它可能會按照您想要的方式運作:IFS=$'\n\t'
。該-print0
選項基本上刪除了所有行分隔符,因此您想要從這些命令中獲得的所有內容根本沒有分隔符,除非檔案名稱中包含空格。對於你的第二組問題:
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
該命令無法正常運行,因為 bash 是這樣讀取它的:
IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done
第一行將 IFS 設為:$'\n' for i in
。之後,下一行讀取為:do echo $i
,這不起作用,因為該do
命令需要某種循環作為前面的命令。下一個之所以有效,是因為分號告訴 bash 指令已到達結尾,因此:
IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done
最後,我認為在設定 IFS 時,'' 與 $'\0' 相同,我的信念是否正確? (我在前面的示例中嘗試了兩者。)如果是這樣,為什麼我顯然需要 $'\n' 而不僅僅是 \n?
是的,第一部分,''
和'\0'
都被認為是NULL,所以它們基本上是相同的。這是由於 bash 解釋$''
為 ANSI C 字串。並且您需要在 \n 兩邊加引號,以使 bash 將其解釋為 C 字串,以便將換行符號放置在 IFS 中。
您可以閱讀有關這些的更多信息這裡。