2011-05-25 92 views
5

這個問題有3個部分,每個單是容易的,但組合在一起是不平凡的(至少對我來說):)如何處理shell腳本參數中的「 - 」?

需要編寫一個腳本,應採取什麼作爲它的參數:

  1. 一個另一個命令名稱
  2. 幾個參數的命令文件的
  3. 列表

例子:

./my_script head -100 a.txt b.txt ./xxx/*.txt 
./my_script sed -n 's/xxx/aaa/' *.txt 

等等。

裏面我由於某種原因,我需要區分

  • 什麼命令
  • 什麼是該命令的參數腳本
  • 哪些文件

所以可能是最標準方式寫上面的例子是:

./my_script head -100 -- a.txt b.txt ./xxx/*.txt 
./my_script sed -n 's/xxx/aaa/' -- *.txt 

問題1:這裏有更好的解決方案嗎?

處理在./my_script(第一嘗試):

command="$1";shift 
args=`echo $* | sed 's/--.*//'` 
filenames=`echo $* | sed 's/.*--//'` 

#... some additional processing ... 

"$command" "$args" $filenames #execute the command with args and files 

filenames將包含spaces該解決方案將失效和/或 ' - ',例如
/一些 - 路徑/到/多/白癡的文件name.txt

問題2:如何正確地獲得$command其爲以後執行$args$filenames

問題3: - 如何實現以下執行風格?

echo $filenames | $command $args #but want one filename = one line (like ls -1) 

這裏是很好的shell解決方案,或者需要使用例如perl嗎?

+1

這將需要一段時間才能回答正確。 1.爲什麼使用back-tics?它們已被棄用,除非您希望以root身份在Solaris或AIX上執行大量工作,而不是必需的。使用$(cmd)! 2.(更多的問題),閱讀getopt和getopts(用於shell)。我在這裏看到了很多例子,至少有一個例子非常詳細。再加上'while(($#> 0))這樣的東西來做processArgs;轉移;完成「將有所幫助。 3.考慮使用案例陳述。對於'--',你可能不得不轉義它們或者引用它們,或者把它轉換成像'[ - ] [ - ]這樣的charclass。 – shellter 2011-05-25 12:20:44

回答

6

首先,它聽起來像是在編寫一個腳本,它接受一個命令和一個文件名列表,然後依次在每個文件名上運行該命令。這可以在一個行完成在bash:

 
$ for file in a.txt b.txt ./xxx/*.txt;do head -100 "$file";done 
$ for file in *.txt; do sed -n 's/xxx/aaa/' "$file";done 

不過,也許我誤解你的意圖,所以讓我逐一回答你的問題。

而不是使用 「 - 」(已具有不同的含義),下面的語法感覺更自然的對我說:

 
./my_script -c "head -100" a.txt b.txt ./xxx/*.txt 
./my_script -c "sed -n 's/xxx/aaa/'" *.txt 

要提取的參數在bash,使用getopts

SCRIPT=$0 

while getopts "c:" opt; do 
    case $opt in 
     c) 
      command=$OPTARG 
      ;; 
    esac 
done 

shift $((OPTIND-1)) 

if [ -z "$command" ] || [ -z "$*" ]; then 
    echo "Usage: $SCRIPT -c <command> file [file..]" 
    exit 
fi 

如果你想運行的每個其餘參數的命令,它應該是這樣的:

for target in "[email protected]";do 
    eval $command \"$target\" 
done 

如果你想閱讀從標準輸入文件名,它看起來更像是這樣的:

while read target; do 
    eval $command \"$target\" 
done 
+0

@TomShaw:很好的第一篇文章。對於最後一塊代碼,也許你想用'eval'作爲循環內部的前綴。謝謝你做了大量的上市。 ;-) – shellter 2011-05-25 13:24:31

+0

@shellter:感謝您的反饋! – 2011-05-25 13:48:55

+0

@ jm666:這個更新版本現在應該處理空格。 – 2011-05-25 13:49:58

0

問題1

我不這麼認爲,至少不是如果你需要爲任意命令執行此操作。

問題3

command=$1 
shift 
while [ $1 != '--' ]; do 
    args="$args $1" 
    shift 
done 
shift 
while [ -n "$1" ]; do 
    echo "$1" 
    shift 
done | $command $args 

問題2

如何從問題3有什麼不同?

+0

echo $ filenames | sed - 如果文件名包含空格,將會中斷。例如echo「file1.txt」「file2 with spaces.txt」 - 因此問 - 我的第一次嘗試是錯誤的。 – jm666 2011-05-25 12:42:41

+0

啊我看到了,編輯我的回答 – 2011-05-25 13:24:25

3

[email protected]變量,當報價就能組參數,因爲它們應該是:

for parameter in "[email protected]" 
do 
    echo "The parameter is '$parameter'" 
done 

如果給定:

head -100 test this "File name" out 

將打印

the parameter is 'head' 
the parameter is '-100' 
the parameter is 'test' 
the parameter is 'this' 
the parameter is 'File name' 
the parameter is 'out' 

現在,你所要做的就是解析循環。你可以使用一些很簡單的規則:

  1. 第一個參數始終是文件名
  2. 是遵循開始與破折號的參數參數
  3. 後以「 - 」或一次一個沒有按」以「 - 」開頭,其餘都是文件名。

您可以檢查,看看是否在參數的第一個字符是一個破折號通過使用這樣的:

if [[ "x${parameter}" == "x${parameter#-}" ]] 

如果你以前沒見過這種語法,這是一個左過濾器#劃分變量名稱的兩個部分。第一部分是變量的名稱,第二部分是glob過濾器(不正則表達式)中斷。在這種情況下,這是一個單一的破折號。只要這個陳述不是真的,你就知道你有一個參數。順便說一句,x可能或可能不需要在這種情況下。當你運行一個測試,並且你有一個破折號的字符串時,測試可能會把它誤認爲是測試的參數而不是值。

放在一起會是這樣的:

parameterFlag="" 
for parameter in "[email protected]"  #Quotes are important! 
do 
    if [[ "x${parameter}" == "x${parameter#-}" ]] 
    then 
     parameterFlag="Tripped!" 
    fi 
    if [[ "x${parameter}" == "x--" ]] 
    then 
     print "Parameter \"$parameter\" ends the parameter list" 
     parameterFlag="TRIPPED!" 
    fi 
    if [ -n $parameterFlag ] 
    then 
     print "\"$parameter\" is a file" 
    else 
     echo "The parameter \"$parameter\" is a parameter" 
    fi 
done 
+0

只有古代炮彈才需要'x'劈啪聲。請不要使用它,它會使代碼變得醜陋。 – nyuszika7h 2014-07-09 21:32:48

+0

@ nyuszika7h,...好 - 那裏有一些角落的情況,它可以在現代shell中使用,但這些情況只存在於'[]'(而不是'[[]]')中,而'test'傳遞了三個以上的參數(因此,當使用'-a'或'-o'時)...這是POSIX最新版本爲什麼將它們描述爲不推薦使用的一部分。 – 2014-09-15 17:41:55