Bash

為 bash 和 qmake 中的變數轉義雙引號

  • May 8, 2017

我為 qmake 編寫了以下元項目文件,該文件旨在建構.pro以下子目錄中的所有文件(由於歷史原因和其他工具鏈,文件名與文件夾名稱不匹配)。這基本上歸結為這樣的事情(拋開項目特定的東西)

THIS_FILE=make-all.pro
TEMPLATE=subdirs
FIND= "find -name \'*.pro\' -printf \'%p\\n\'"
AWK= "awk \'{$1=substr($1,3); print}\'"
RMTHIS= "awk \'!/$$THIS_FILE/\'"
SUBDIRS= $$system($$FIND | $$AWK | $$RMTHIS )

我需要獲取包含.proBash 腳本中文件的文件夾列表,因此我決定複製該方法

#!/bin/bash
FIND="find -name '*.pro' -printf '%h\\n"
AWK="awk '{\$1=substr(\$1,3);printf}'"
SUBDIRS=$($FIND | $AWK)

顯然這不起作用,在表達式awk中噴出錯誤。invalid char ''嘗試直接在 Bash 中執行相同的行表明awk只有在使用雙引號時才有效

find -name '*.pro' | awk "{\$1=substr(\$1,3);printf}"

將有問題的行替換為

AWK='awk "{$1=substr($1,3);printf}" '

沒有工作結果,腳本的輸出為空,與手動輸入命令的輸出不同。顯然

find -name '*.pro' 

In script 僅在目前文件夾中查找文件,而在 bash 命令行中的對應項在子文件夾中查找它。出了什麼問題以及為什麼 qmake 的工作方式也不同?

我不熟悉 qmake 語法,但是從您的範例中,它以與 shell 完全不同的方式使用引號和變數。所以你不能只使用相同的程式碼。

http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters涵蓋了您需要知道的內容,所以在這裡我將只總結與您的問題相關的內容。

簡而言之,您不能簡單地將 shell 命令填充到字元串中。諸如 bash 之類的 shell 不會遞歸地解析字元串。他們解析原始碼並將命令建構為字元串列表:一個簡單的命令由命令名稱(執行檔的路徑、函式名稱等)及其參數組成。

當您使用類似的賦值時AWK="awk '{\$1=substr(\$1,3);printf}'",這會將變數AWK設置為字元串;當您將變數用作$AWK外部雙引號時,這會通過在空格處解析變數的值將其轉換為字元串列表,但它不會將其解析為 shell 程式碼,因此像這樣的字元'最終會出現在參數中。這很少是可取的,這就是為什麼在 shell 中使用變數的一般建議是**在變數擴展周圍加上雙引號,**除非您知道需要將它們關閉並且您了解這意味著什麼。(請注意,我在這裡的回答並不能說明全部。)

在 bash 中,您可以將一個簡單的命令填充到一個數組中。

#!/bin/bash
FIND=(find -name '*.pro' -printf '%h\\n')
AWK=(awk '{$1=substr($1,3);print}')
SUBDIRS=$("${FIND[@]}" | "${AWK[@]}") 

但通常儲存命令的最佳方式是定義一個函式。這不僅限於一個簡單的命令:通過這種方式,您可以進行重定向、變數賦值、條件等。

#!/bin/bash
find_pro_files () {
 find -name '*.pro' -printf '%h\\n'
}
truncate_names () {
 awk '{$1=substr($1,3);print}'
}
SUBDIRS=$(find_pro_files | truncate_names)

我不確定你想用這個腳本做什麼(特別是考慮到你改變了程式碼片段之間的findandawk程式碼),但可能有更好的方法來做到這一點。您可以使用循環遍歷*.pro目前目錄的子目錄中的文件

for pro in */*.pro; do
 subdir="${pro%/*}"
 basename="${pro##*/}"
 …
done

如果您想遞歸地遍歷子目錄,在 bash 中,您可以使用**/*.pro而不是*/*.pro,但請注意,這也會遞歸到目錄的符號連結。

引用自:https://unix.stackexchange.com/questions/362843