这是我的任务。任务是从输入文件中输出 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。
您可能想使用getopt
not 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
仔细阅读手册页:
一个简单的短选项是“
-
”后跟一个短选项字符。如果选项有必需的参数,则可以直接将其写在选项字符之后或作为下一个参数(即,在命令行上用空格分隔)。 如果选项有可选参数,则必须将其直接写在选项字符(如果存在)之后。长选项通常以“
--
”开头,后跟长选项名称。如果选项有必需的参数,则可以直接将其写在长选项名称之后,用“=
”分隔,或者作为下一个参数(即在命令行上用空格分隔)。 如果选项有可选参数,则必须直接写在长选项名后面,用 '=
' 分隔,如果存在(如果添加“=
”但其后面没有任何内容,则会被解释为好像没有参数存在;这是一个小错误,请参阅错误)。