我必须编写一个 bash 脚本来计算来自标准输入的“<”和“>”等符号。
例如:
$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!
我这样做了:
x=`grep -o '>' example.html | wc -l`
y=`grep -o '<' example.html | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
这是好方法吗?我不知道如何从标准输入获取文件名“example.html”。
答案1
重点是stdin
,它可以是任何东西,例如,它可以是管道、网络套接字、常规文件、设备,当脚本启动时,它可以是常规文件的一半......如果可以的话如果您不能一次性处理数据,那么您就将自己限制为可寻找的文件,即常规文件和一些设备文件,或者必须以某种方式存储信息(在临时文件或内存中......)。不过,在这里可以立即获取所有信息。
例如,你可以这样做:
$ grep -o '[<>]' < a.html | sort | uniq -c
82 <
82 >
POSIXly:
fold -w 1 a.html | grep '[<>]' | sort | uniq -c
要检测不匹配:
if fold -w 1 a.html | awk '{n[$0]++}
END{exit(n["<"]!=n[">"])}'
then
echo match
else
echo mismatch
fi
现在,为了回答本主题中的问题,在 Linux 上,您可以使用以下命令找到 stdin 的“名称”:
readlink -f /dev/stdin
例子:
$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]
(上面的20238是 的pid readlink
,所以退出后该路径就没有多大用处了readlink
,反正也不会,这pipe:[758683]
只是信息性的,不能是打开)。
更一般地说,如果lsof
可用:
lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'
(不过,$$
作为执行 shell 的进程的 pid,它在标准输入重定向的子 shell 中不起作用)
现在,您不一定能够重新打开该文件进行读取,即使您这样做,从该文件读取也可能不会再次为您提供相同的数据(例如,考虑管道)。
$ seq 3 > a
$ { cat; cat /dev/stdin; } < a
1
2
3
1
2
3
$ cat a | { cat; cat /dev/stdin; }
1
2
3
在 Linux 上,/dev/stdin
如果 stdin 是常规文件,则打开,将从头开始再次读取该文件,而在其他系统上,打开 /dev/stdin 更像是dup(0)
,即它不会将文件倒回到开头(在上面的第一个示例中) ,它将输出1\n2\n3\n
一次而不是两次)。
答案2
您必须以某种方式存储文件内容。您可以使用变量。
content=`cat`
x=`echo "$content" | grep -o '>' | wc -l`
y=`echo "$content" | grep -o '<' | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
或临时文件(如果example.html
包含空字节则必需)。
tmp=`mktemp`
trap "rm $tmp" EXIT
x=`grep -o '>' "$tmp" | wc -l`
y=`grep -o '<' "$tmp" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
如果不需要从标准输入读取文件内容,您可以将文件名作为参数传递给脚本。
x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
像这样调用脚本:
$ ./myscript.sh example.html
答案3
您的任务的一种可能性是:
#!/bin/bash
if [[ -n $1 ]]; then
if [[ ! -f $1 ]] || [[ ! -r $1 ]]; then
echo >&2 "File \`$1' not found or not readable."
exit 1
fi
exec "$0" < "$1"
fi
declare -A hary
while read c; do
(( ++hary[$c] ))
done < <(grep -o '[<>]')
echo "> ${hary[>]}"
echo "< ${hary[<]}"
如果你调用此脚本计数不匹配(您可以选择一个较短的名称),您将能够使用带或不带文件名的文件名。几种可能性:
$ countmismatched example.html
$ countmismatched < example.html
$ cat example.html | countmismatched
输出将类似于:
> 41
< 42
如果需要检测不匹配,请在脚本末尾添加:
if (( hary[<]} != hary[>] )); then
echo "Mismatched brackets"
else
echo "It's all good"
fi
或者更明确的东西:
((difference=hary[<]-hary[>]))
if (( difference>0 )); then
echo "Mismatched brackets: you have $difference more <'s than >'s"
elif (( difference<0 )); then
echo "Mismatched brackets: you have $((-difference)) more >'s than <'s"
else
echo "It's all good"
fi