如何正确识别参数的顺序?

如何正确识别参数的顺序?

这是我的任务。任务是从输入文件中输出 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

哪些是正确的。另外,如果参数中没有文件,则使用标准输入,如何处理?

答案1

你的-n选择需要一个参数,所以你需要getopts 'n:' arg.

选项参数可在 中找到$OPTARG

不要触摸OPTIND循环while 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并将其作为管道中自己的阶段运行)。

答案2

如果 n 没有参数,则 n 的默认值是 5。

您可能想使用getoptnot getopts。它不是 bash 内置函数,因此请阅读其手册页并查看示例代码

鉴于此示例脚本

#!/usr/bin/env bash

tmp=$(getopt -o n:: --long n:: -- "$@")

if [[ $? -ne 0 ]]; then 
    echo "usage: ..." >&2
    exit 1
fi

eval set -- "$tmp"

n=0

while :; do
    case "$1" in
        -n|--n) n=${2:-5}; shift 2 ;;
        --)     shift; break ;;
        *)      echo "error" >&2; exit 1 ;;
    esac
done

echo "n = $n"
echo "remaining args:"
printf "%s\n" "$@"

双冒号表示该选项采用可选参数。看看以各种方式调用时会发生什么:

$ bash sample.sh foo bar
n = 0
remaining args:
foo
bar
$ bash sample.sh -n foo bar
n = 5
remaining args:
foo
bar
$ bash sample.sh -n 10 foo bar
n = 5
remaining args:
10
foo
bar
$ bash sample.sh -n10 foo bar
n = 10
remaining args:
foo
bar
$ bash sample.sh --n foo bar
n = 5
remaining args:
foo
bar
$ bash sample.sh --n 10 foo bar
n = 5
remaining args:
10
foo
bar
$ bash sample.sh --n=10 foo bar
n = 10
remaining args:
foo
bar
$ bash sample.sh --n= foo bar
n = 5
remaining args:
foo
bar

仔细阅读手册页:

一个简单的短选项是“ -”后跟一个短选项字符。如果选项有必需的参数,则可以直接将其写在选项字符之后或作为下一个参数(即,在命令行上用空格分隔)。 如果选项有可选参数,则必须将其直接写在选项字符(如果存在)之后

长选项通常以“ --”开头,后跟长选项名称。如果选项有必需的参数,则可以直接将其写在长选项名称之后,用“ =”分隔,或者作为下一个参数(即在命令行上用空格分隔)。 如果选项有可选参数,则必须直接写在长选项名后面,用 ' =' 分隔,如果存在(如果添加“ =”但其后面没有任何内容,则会被解释为好像没有参数存在;这是一个小错误,请参阅错误)。

相关内容