Bash,标准输入文件的名称?

Geo*_*rgy 4 bash stdin

我必须编写一个 bash 脚本,该脚本将从标准输入中计算诸如“<”和“>”之类的符号。

例如:

$ ./myscript.sh <example.html
> - 20
< - 21
Found mismatching brackets!
Run Code Online (Sandbox Code Playgroud)

我这样做了:

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
Run Code Online (Sandbox Code Playgroud)

这是好方法吗?我不知道如何从 stdin 获取文件名“example.html”。

Sté*_*las 15

重点stdin是它可以是任何东西,例如可以是管道、网络套接字、常规文件、设备,当您的脚本启动时,它可以是常规文件的一半......如果可以的话t 一次性处理数据,然后您将自己限制为可查找文件,即常规文件和一些设备文件,或者必须以某种方式存储信息(在临时文件或内存中......)。不过,在这里可以一次获取所有信息。

例如,你可以这样做:

$ grep -o '[<>]' < a.html | sort | uniq -c
     82 <
     82 >
Run Code Online (Sandbox Code Playgroud)

POSIXly:

fold -w 1 a.html | grep '[<>]' | sort | uniq -c
Run Code Online (Sandbox Code Playgroud)

检测不匹配:

if fold -w 1 a.html | awk '{n[$0]++}
     END{exit(n["<"]!=n[">"])}'
then
  echo match
else
  echo mismatch
fi
Run Code Online (Sandbox Code Playgroud)

现在,要回答主题中的问题,在 Linux 上,您可以使用以下命令找到 stdin 的“名称”:

readlink -f /dev/stdin
Run Code Online (Sandbox Code Playgroud)

例子:

$ readlink -f /dev/stdin < a
/home/chazelas/a
$ : | readlink -f /dev/stdin
/proc/20238/fd/pipe:[758683]
Run Code Online (Sandbox Code Playgroud)

(上面的 20238 是 的 pid readlink,所以该路径在readlink退出后不会有太大用处,反正也不会,那pipe:[758683]只是提供信息,无法打开)。

更普遍的lsof是,如果可用:

lsof -ad0 -p "$$" -Fn 2> /dev/null | sed -n 'n;s/^n//p'
Run Code Online (Sandbox Code Playgroud)

(虽然,$$作为执行 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
Run Code Online (Sandbox Code Playgroud)

在 Linux 上,/dev/stdin如果 stdin 是常规文件,则打开将再次从头开始读取文件,而在其他系统上,打开 /dev/stdin 更像是一个dup(0),即它不会将文件倒回开头(在上面的第一个示例中) ,它将输出1\n2\n3\n一次而不是两次)。


小智 7

您将不得不以某种方式存储文件内容。您可以使用变量。

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
Run Code Online (Sandbox Code Playgroud)

或者一个临时文件(如果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
Run Code Online (Sandbox Code Playgroud)

如果不需要从 stdin 读取文件内容,您可以将文件名作为参数传递给脚本。

x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
  echo "Mismatch!"
fi
echo $x
echo $y
Run Code Online (Sandbox Code Playgroud)

像这样调用脚本:

$ ./myscript.sh example.html
Run Code Online (Sandbox Code Playgroud)