如何编写一个接受来自文件名参数或标准输入的脚本?
例如,您可以使用less
这种方式。可以less filename
等效地执行和cat filename | less
。
有没有一种简单的“开箱即用”方法可以做到这一点?还是我需要重新发明轮子并在脚本中编写一些逻辑?
答案1
如果文件参数是脚本的第一个参数,请测试是否存在参数 ( $1
) 并且它是否为文件。否则从 stdin 读取输入 -
因此你的脚本可能包含如下内容:
#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input
例如,您可以像这样调用脚本
./myscript.sh filename
或者
who | ./myscript.sh
编辑 对脚本的一些解释:
[ $# -ge 1 -a -f "$1" ]
- 如果至少有一个命令行参数($# -ge 1
)AND(-a 运算符)第一个参数是一个文件(-f 测试“$1”是否是一个文件)那么测试结果为真。
&&
是 shell 逻辑 AND 运算符。如果测试为真,则分配input="$1"
并cat $input
输出文件。
||
是 shell 逻辑或运算符。如果测试为假,则||
解析后面的命令。输入被赋值为“-”。命令cat -
从键盘读取。
总结一下,如果提供了脚本参数并且它是一个文件,那么变量输入将被分配给文件名。如果没有有效的参数,那么 cat 将从键盘读取。
答案2
read
./script <someinput
从标准输入读取。从文件 ( ) 或通过管道 ( )重定向dosomething | ./script
不会使其工作方式不同。
您所要做的就是循环遍历输入中的所有行(这与遍历文件中的行没有什么区别)。
(示例代码,仅处理一行)
#!/bin/bash
read var
echo $var
将回显标准输入的第一行(通过<
或|
)。
答案3
你没有提到你计划使用什么 shell,因此我假设使用 bash,尽管这些是跨 shell 的相当标准的东西。
文件参数
可以通过变量访问参数$1
- $n
($0
返回用于运行程序的命令)。假设我有一个脚本,它只cat
输出 n 个文件,每个文件之间有一个分隔符:
#!/usr/bin/env bash
#
# Parameters:
# 1: string delimiter between arguments 2-n
# 2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
cat $arg
echo $1
done
在本例中,我们将文件名传递给 cat。但是,如果你想转换文件中的数据(而不显式写入和重写),你也可以将文件内容存储在变量中:
file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename
从标准输入读取
至于从标准输入读取,大多数 shell 都有一个非常标准的read
内置命令,尽管在提示符的指定方式上存在差异(至少)。
这Bash 内置命令手册页有一个非常简洁的解释read
,但我更喜欢打击黑客页。
简单地:
read var_name
多个变量
要设置多个变量,只需提供多个参数名称即可read
:
read var1 var2 var3
read
然后将把来自 stdin 的一个单词放入每个变量中,将所有剩余的单词转储到最后一个变量中。
λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5
如果输入的单词数少于变量数,则剩余的变量将为空(即使先前设置过):
λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line
提示
我-p
经常使用标志来提示:
read -p "Enter filename: " filename
注意:ZSH 和 KSH(也许还有其他)使用不同的提示语法:
read "filename?Enter filename: " # Everything following the '?' is the prompt
默认值
这其实不算什么read
技巧,但我经常与 结合使用read
。例如:
read -p "Y/[N]: " reply
reply=${reply:-N}
基本上,如果变量(reply)存在,则返回其自身,但如果为空,则返回以下参数(“N”)。
答案4
您还可以执行以下操作:
#!/usr/bin/env bash
# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"
# You can now use "$input_file" as your file to operate on
cat "$input_file"
有关 Bash 中更多巧妙的参数替换技巧,请参阅这。