Bash

Shell 函式:作為參數的管道序列

  • January 29, 2019

我有一個 shell 函式(在 .bashrc 中),它創建一個臨時文件,執行參數(包括所有管道序列),將其重定向到一個臨時文件,然後在 VS Code 中打開它。

我呼叫 shell 函式為

Temp "ls | nl"

在下面的程式碼中,我試圖讓它工作。我用 IFS 將整個字元串分解為一個空格,將其儲存在一個數組中。現在我想執行整個管道序列並將最終的標準輸出重定向到臨時文件。我怎樣才能做到這一點。?我知道 for 循環在這裡不起作用。但我想列印整個數組。

Temp() {
   a="$(mktemp --suffix=.md "/tmp/$1-$(GetFilename "$2")-XXX")"
   IFS=' ' read -r -a Array <<< "$1"
   nArray=${#Array[@]} # Size of array
   for ((i=0; i<nArray; i=i+1)); do

   done
   #"${@}" > "$a"
   code -r "$a"
}

在互動式終端會話中,以下工作:

cat <<EOF
$(ls | nl)
EOF

所以,我嘗試了 Heredoc,而不是函式內的 for 循環

cat > "$a" <<EOF
$($1)
EOF

但這會引起引號|,因此失敗了。

如果我們可以以某種方式刪除引號,上述方法將起作用。


這也行不通

cat > "$a" <<EOF
$(echo $1 | sed "s/'//g")
EOF

如果您希望函式執行作為參數給出的 shell 命令,則需要eval在字元串上使用,例如列印FOO

eval_arg() {
   eval "$1"
}
eval_arg "echo foo |tr a-z A-Z"

僅僅擴展"$1"是行不通的,因為像管道、引號和重定向這樣的 shell 語法在參數擴展之後不會被處理。將第一個參數拆分為數組或拆分$@並不會真正改變這一點。


因此,$(ls | nl)有一個逐字管道字元,它在 shell 中處理,但在 中$($1),值中的任何特殊字元$1都不會被處理(除了分詞和 glob)。這裡沒有引用。

至於最後一個範例,$(echo $1 | sed "s/'//g")拆分$1on whitespace 的值(使用 default 進行單詞拆分IFS),然後將單詞與空格 ( echo) 連接起來,將其作為輸入傳遞給sed它,從而刪除那裡的任何單引號。

因此:

$ set -- "'foo  bar'"
$ $(echo $1 | sed "s/'//g")
foo bar 

命令中的外引號set由 shell 處理,而內引號是值的一部分。請注意,justset -- 'foo bar'不會導致$1包含任何引號,此處唯一的引號將作為正常 shell 處理的一部分被刪除。

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