這是我的問題的后續行動:How to write bash function to print and run command when command has arguments with space or things to be expand
假設我有這個函式來列印和運行存盤在陣列中的命令:
# Print and run the cmd stored in the passed-in array
print_and_run() {
echo "Running cmd: $*"
# run the command by calling all elements of the command array at once
"$@"
}
這作業正常:
cmd_array=(ls -a /)
print_and_run "${cmd_array[@]}"
但這不起作用:
cmd_array=(ls -a / | grep "home")
print_and_run "${cmd_array[@]}"
錯誤syntax error near unexpected token `|'::
eRCaGuy_hello_world/bash$ ./print_and_run.sh
./print_and_run.sh: line 55: syntax error near unexpected token `|'
./print_and_run.sh: line 55: `cmd_array=(ls -a / | grep "home")'
如何讓這個概念與|命令中的管道運算子 ( ) 一起使用?
uj5u.com熱心網友回復:
如果要將僅包含的陣列元素|視為生成管道的指令,則可以這樣做。我不推薦它——這意味著如果你不驗證字串中的變數不能只包含一個管道字符,那么你就有安全風險——但這是可能的。
下面,我們創建了一個隨機的一次性"$pipe"符號來使攻擊更加困難。如果您不愿意這樣做,請更改[[ $arg = "$pipe" ]]為[[ $arg = "|" ]].
# generate something random to make an attacker's job harder
pipe=$(uuidgen)
# use that randomly-generated sigil in place of | in our array
cmd_array=(
ls -a /
"$pipe" grep "home"
)
exec_array_pipe() {
local arg cmd_q
local -a cmd=( )
while (( $# )); do
arg=$1; shift
if [[ $arg = "$pipe" ]]; then
# log an eval-safe copy of what we're about to run
printf -v cmd_q '%q ' "${cmd[@]}"
echo "Starting pipeline component: $cmd_q" >&2
# Recurse into a new copy of ourselves as a child process
"${cmd[@]}" | exec_array_pipe "$@"
return
fi
cmd =( "$arg" )
done
printf -v cmd_q '%q ' "${cmd[@]}"
echo "Starting pipeline component: $cmd_q" >&2
"${cmd[@]}"
}
exec_array_pipe "${cmd_array[@]}"
在https://ideone.com/IWOTfO的在線沙箱中查看此運行
uj5u.com熱心網友回復:
一般的方向是你沒有。您不會存盤整個命令列以供以后列印,這不是您應該采取的方向。
“壞”的解決方案是使用eval.
“好”的解決方案是將文字'|'字符存盤在陣列中(或更好的表示形式)并決議陣列,提取管道部分并執行它們。查爾斯在另一個驚人的答案中提出了這一點。它只是重寫shell中已經存在的決議器。它需要大量作業,擴展它需要大量作業。
最終結果是,您正在外殼內重新實作外殼的一部分。基本上在shell中撰寫一個shell解釋器。此時,您可以考慮采用 Bash 源并shopt -o print_the_command_before_executing在源中實作一個新選項,這可能會更簡單。
但是,我相信最終目標是為用戶提供一種查看正在執行的內容的方法。我建議像.gitlab-ci.yml處理script:陳述句一樣處理它。如果您想通過“除錯”支持來發明自己的語言,那么就這樣做而不是半途而廢。考慮以下 YAML 檔案:
- ls -a / | grep "home"
- echo other commands
- for i in "stuff"; do
echo "$i";
done
- |
for i in "stuff"; do
echo "$i"
done
然后是以下“跑步者”:
import yaml
import shlex
import os
import sys
script = []
input = yaml.safe_load(open(sys.argv[1], "r"))
for line in input:
script = [
"echo " shlex.quote(line).replace("\n", "<newline>"), # some unicode like ? would look nice
line,
]
os.execvp("bash", ["bash", "-c", "\n".join(script)])
執行 runner 會導致:
ls -a / | grep "home"
home
echo other commands
other commands
for i in "stuff"; do echo "$i"; done
stuff
for i in "stuff"; do<newline> echo "$i"<newline>done<newline>
stuff
這提供了更大的靈活性并且相當簡單,可以輕松支持任何外殼構造。您可以在他們的存盤庫上嘗試 gitlab-ci/cd 并閱讀檔案。
YAML 格式只是輸入格式的一個示例。在部分之間使用特殊注釋# --- cut ---并使用決議器提取每個部分將允許在腳本上運行 shellcheck。echo您可以互動式地運行 Bash,列印要執行的部分,然后將要執行的部分“輸入”到互動式 Bash,而不是生成帶有陳述句的腳本。這將有助于保存$?。
無論哪種方式 - 使用“好的”解決方案,您最終都會得到一個自定義決議器。
uj5u.com熱心網友回復:
改為這樣做。有用。
print_and_run() {
echo "Running cmd: $1"
eval "$1"
}
示例用法:
cmd='ls -a / | grep -C 9999 --color=always "home"'
print_and_run "$cmd"
輸出:
Running cmd: ls -a / | grep -C 9999 --color=always "home"
(rest of output here, with the word "home" highlighted in red)
uj5u.com熱心網友回復:
declare -f您可以傳遞整個函式并將輸出與一些自定義決議一起使用,而不是傳遞陣列:
print_and_run() {
echo " $(
declare -f "$1" |
# Remove `f() {` and `}`. Remove indentation.
sed '1d;2d;$d;s/^ *//' |
# Replace newlines with <newline>.
sed -z 's/\n*$//;s/\n/<newline>/'
)"
"$@"
}
cmd() { ls -a / | grep "home"; }
print_and_run cmd
結果是:
ls --color -F -a / | grep "home"
home/
它將允許支持任何 shell 構造,并且仍然允許您使用 shellcheck 檢查它,并且不需要那么多作業。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/425937.html
下一篇:jq更新json檔案中的布林值
