Bash

如何正確辨識參數的順序?

  • August 8, 2019

這是我的任務。任務是從輸入文件中輸出 n 行最長的行。如果 n 沒有參數,則 n 的預設值為 5。如果參數中沒有文件,則使用標準輸入。如果至少有 2 個文件,則輸出名稱文件。輸出行的順序應與原始文件中的順序相同。我在這裡問了一個問題來處理可選參數:如何處理 shell 腳本中的可選輸入?

但是,我遇到了新的 shell 腳本的另一個問題。

#!/bin/sh
while getopts “n” arg; do
 case $arg in
 n)
   # Check next positional parameter
   eval nextopt=\${$OPTIND}
   # existing or starting with dash?
   if [[ -n $nextopt && $nextopt != -* ]] ; then
     OPTIND=$((OPTIND + 1))
     level=$nextopt
   else
     level=5
   fi
   ;;
 esac
done

for name do
   if [ "$#" -gt 1 ]; then
       printf 'File: %s\n' "$name"
   fi
awk '{ print length(), NR, $0 | "sort -rn" }' $name | head -n $level | sed 's/[^ ]* //' | sort -n

done

我這樣執行

sh ex1.sh -n 10 unix1.txt unix1.1.txt

這是輸出

File: -n
awk: can't open file -n
source line number 1
File: 10
awk: can't open file 10
source line number 1
File: unix1.txt
2 kbjkbkbbnbnmbnmnmmnbmnbmjbjkb
3 asjdsakdbakjsdbasbkj
4 asjdsakdbakjsdbasbkj
5 asjdsakdbakjsdbasbkj
10 ppûunsdj
11 tieutuvi
13 sdbhsdbjhdsvfdsvfgj
14 avavdvas
16 ffdsdfggdgdgdfgdfgdf112233
17 qwertyuiopsdfghjklxcvbnm,fghjk
File: unix1.1.txt
1 csdkbfsdk
2 fskjfnjkfnkjdsndjks
3 fsnjfnsjkf
4 snjfndsjknskjdfbnjksfdsfn
5 323124
6 jknjkkjnk4n4jn2
7 kjnjkb423
13 423b2j3kb4jk23bkb234kb32
14 234jb32jk43b
15 331

“-n”和“10”不是文件。另外,如果我這樣跑

sh ex1.sh -n unix1.txt unix1.1.txt

輸出應該是文件中最長的 5 行,而是:

File: -n
head: illegal line count -- unix1.txt
awk: can't open file -n
source line number 1
File: unix1.txt
head: illegal line count -- unix1.txt
File: unix1.1.txt
head: illegal line count -- unix1.txt

那麼我該如何解決這個問題呢?雖然這不是目標,但這會奏效

while getopts “n” arg; do
 case $arg in
 n)
   # Check next positional parameter
   eval nextopt=\${$OPTIND}
   # existing or starting with dash?
   if [[ -n $nextopt && $nextopt != -* ]] ; then
     OPTIND=$((OPTIND + 1))
     level=$nextopt
   else
     level=5
   fi
   ;;
 esac
done

awk '{ print length(), NR, $0 | "sort -rn" }' unix1.txt | head -n $level | sed 's/[^ ]* //' | sort -n

如果我跑

sh ex1.sh -n 

我有

2 kbjkbkbbnbnmbnmnmmnbmnbmjbjkb
4 asjdsakdbakjsdbasbkj
5 asjdsakdbakjsdbasbkj
16 ffdsdfggdgdgdfgdfgdf112233
17 qwertyuiopsdfghjklxcvbnm,fghjk

或者

sh ex1.sh -n 10

我有

2 kbjkbkbbnbnmbnmnmmnbmnbmjbjkb
3 asjdsakdbakjsdbasbkj
4 asjdsakdbakjsdbasbkj
5 asjdsakdbakjsdbasbkj
10 ppûunsdj
11 tieutuvi
13 sdbhsdbjhdsvfdsvfgj
14 avavdvas
16 ffdsdfggdgdgdfgdfgdf112233
17 qwertyuiopsdfghjklxcvbnm,fghjk

這是正確的。另外,如何處理“如果參數中沒有文件,則使用標準輸入”?

您的-n選項需要一個參數,因此您需要getopts 'n:' arg.

選項參數位於$OPTARG.

不要OPTINDwhile getopts循環中觸摸。

循環之後,shift "$(( OPTIND - 1 ))". 這會將文件名留在位置參數中。

那是,

#!/bin/sh

level=5
while getopts 'n:' arg; do
   case $arg in
     n) level=$OPTARG ;;
     *) echo 'Error in command line parsing' >&2
   esac
done

shift "$(( OPTIND - 1 ))"

for name do
   # stuff
done

接下來,您永遠不會處理沒有輸入文件的情況。

如果沒有給出,以下使腳本使用標準輸入作為文件名:

if [ "$#" -eq 0 ]; then
   # handle no filenames, for example:
   set -- /dev/stdin
fi

for name do
    # stuff
done

我將把剩下的留給你(但為了清楚起見,我強烈建議將其sort移出awk並將其作為管道中的自己的階段執行)。

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