簡體   English   中英

將多個參數傳遞給UNIX shell腳本

[英]Passing multiple arguments to a UNIX shell script

我有以下(bash)shell腳本,我理想情況下用於按名稱殺死多個進程。

#!/bin/bash
kill `ps -A | grep $* | awk '{ print $1 }'`

但是,雖然這個腳本有效,但是傳遞了一個參數:

結束鉻

(腳本的名稱結束)

如果傳遞了多個參數,它將不起作用:

$ end chrome firefox

grep:firefox:沒有這樣的文件或目錄

這里發生了什么?

我認為$*按順序將多個參數傳遞給shell腳本。 我沒有在輸入中輸入任何內容 - 我要殺死的程序(chrome和firefox)是開放的。

任何幫助表示贊賞。

記住grep對多個參數的作用 - 第一個是要搜索的單詞,其余的是要掃描的文件。

還要記住$*"$*"$@都會在參數中丟失空格,而神奇的"$@"表示法則沒有。

因此,為了處理您的情況,您將需要修改調用grep的方式。 你需要使用grep -F (aka fgrep )和每個參數的選項,或者你需要使用grep -E (aka egrep )進行交替。 在某種程度上,它取決於您是否必須處理自身包含管道符號的參數。

通過單次調用grep可靠地完成這一操作是非常棘手的。 您可能最好能夠容忍多次運行管道的開銷:

for process in "$@"
do
    kill $(ps -A | grep -w "$process" | awk '{print $1}')
done

如果像這樣多次運行ps的開銷太痛苦了(寫它會傷害我 - 但我沒有測量成本),那么你可能會做類似的事情:

case $# in
(0) echo "Usage: $(basename $0 .sh) procname [...]" >&2; exit 1;;
(1) kill $(ps -A | grep -w "$1" | awk '{print $1}');;
(*) tmp=${TMPDIR:-/tmp}/end.$$
    trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15
    ps -A > $tmp.1
    for process in "$@"
    do
         grep "$process" $tmp.1
    done |
    awk '{print $1}' |
    sort -u |
    xargs kill
    rm -f $tmp.1
    trap 0
    ;;
esac

使用普通xargs是可以的,因為它正在處理進程ID列表,而進程ID不包含空格或換行符。 這保留了簡單案例的簡單代碼; 復雜的情況使用臨時文件來保存ps的輸出,然后在命令行中為每個進程名稱掃描一次。 sort -u確保如果某個進程恰好匹配所有關鍵字(例如, grep -E '(firefox|chrome)'將匹配兩者),則只發送一個信號。

陷阱線等確保臨時文件被清理,除非有人對命令過於殘忍(捕獲的信號是HUP,INT,QUIT,PIPE和TERM,又稱1,2,3,13和15;零捕獲shell出於任何原因退出)。 每當腳本創建臨時文件時,您應該對文件的使用進行類似的捕獲,以便在進程終止時將其清除。

如果你感到謹慎並且你有GNU Grep,你可以添加-w選項,以便命令行上提供的名稱只匹配整個單詞。


以上所有內容都適用於Bourne / Korn / POSIX / Bash系列中的幾乎所有shell(您需要使用帶有嚴格Bourne shell的反引號代替$(...) ,以及條件中的前導括號Bourne shell也不允許使用case 但是,您可以使用數組來正確處理事物。

n=0
unset args  # Force args to be an empty array (it could be an env var on entry)
for i in "$@"
do
    args[$((n++))]="-e"
    args[$((n++))]="$i"
done
kill $(ps -A | fgrep "${args[@]}" | awk '{print $1}')

這會仔細保留參數中的間距,並使用完全匹配的進程名稱。 它避免了臨時文件。 顯示的代碼不驗證零參數; 這必須事先完成。 或者你可以添加一行args[0]='/collywobbles/'或類似的東西來提供一個默認的 - 不存在的 - 來搜索命令。

要回答你的問題,正在發生的是$*擴展到參數列表,因此第二個和后面的單詞看起來像grep(1)文件。

要按順序處理它們,您必須執行以下操作:

for i in $*; do
    echo $i
done

通常,在這種情況下,使用"$@" (帶引號)代替$*

man sh ,並查看killall(1)pkill(1)pgrep(1)

相反,請查看pkill(1)killall(1)作為@khachik的評論。

應該很少使用$* 我一般會推薦"$@" Shell參數解析相對復雜且容易出錯。 通常你弄錯的方法是最終評估不應該做的事情。

例如,如果您輸入以下內容:

end '`rm foo`'

你會發現,如果你有一個名為'foo'的文件,你就不會再這樣了。

這是一個腳本,可以完成您要求完成的任務。 如果任何參數包含'\\n''\\0'字符,則會失敗:

#!/bin/sh

kill $(ps -A | fgrep -e "$(for arg in "$@"; do echo "$arg"; done)" | awk '{ print $1; }')

我非常喜歡使用$(...)語法來做反引號。 它更清晰,當你築巢時它也不那么模棱兩可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM