一个人如何编写一个接受来自文件名参数或stdin输入的脚本?
例如,您可以使用less
这种方式。一个人可以less filename
等效地执行cat filename | less
。
是否有一种简单的“开箱即用”方法?还是我需要重新发明轮子并在脚本中写一些逻辑?
一个人如何编写一个接受来自文件名参数或stdin输入的脚本?
例如,您可以使用less
这种方式。一个人可以less filename
等效地执行cat filename | less
。
是否有一种简单的“开箱即用”方法?还是我需要重新发明轮子并在脚本中写一些逻辑?
Answers:
如果file参数是脚本的第一个参数,请测试是否有一个参数($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
)和(-a运算符)的第一个参数是文件(-f测试“ $ 1”是否是文件),则测试结果为true。
&&
是外壳程序逻辑AND运算符。如果test为true,则分配input="$1"
并cat $input
输出文件。
||
是Shell逻辑OR运算符。如果测试为假,则||
分析以下命令。输入分配给“-”。该命令cat -
从键盘读取。
总结一下,如果提供了script参数并且它是一个文件,则将变量输入分配给该文件名。如果没有有效的参数,则cat从键盘读取。
&& input="$1" || input="-"
为什么在test
运营商之外?
$@
)怎么办?
read
从标准输入读取。从文件(./script <someinput
)或通过管道(dosomething | ./script
)重定向它不会使其工作有所不同。
您所需要做的就是遍历输入中的所有行(与遍历文件中的行没有什么不同)。
(示例代码,仅处理一行)
#!/bin/bash
read var
echo $var
将回显您的标准输入的第一行(通过<
或|
)。
您没有提到打算使用哪种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
就读取stdin而言,大多数shell都read
内置了相当标准的内容,尽管指定提示的方式有所不同(至少)。
该猛砸内建手册页有一个非常简明的解释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
经常使用flag作为提示:
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}
基本上,如果变量(答复)存在,则返回自身,但如果为空,则返回以下参数(“ N”)。
最简单的方法是自己重定向stdin:
if [ "$1" ] ; then exec < "$1" ; fi
或者,如果您更喜欢简洁的形式:
test "$1" && exec < "$1"
现在,您脚本的其余部分可以从stdin中读取。当然,您可以通过更高级的选项解析来类似地进行操作,而不是将文件名的位置硬编码为"$1"
。
exec
会尝试将参数作为命令执行,这不是我们想要的。
if [ "$1" ] ; then exec < "$1" ; fi
在测试脚本中进行了复制,但由于命令未知,它会给出错误消息。与简洁形式相同。
使用(或断开连接)已经以此方式运行的其他东西,并使用 "$@"
假设我想编写一个工具,用制表符替换文本中的空格
tr
这样做是最明显的方法,但是它只接受stdin,因此我们必须链接以下内容cat
:
$ cat entab1.sh
#!/bin/sh
cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh
cat "$@"|tr -s ' ' '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh
cat "$@"|tr -s ' ' '\t'
$
对于一个示例,其中所使用的工具已经表现出这种方式,我们可以sed
改为使用以下方式重新实现:
$ cat entab2.sh
#!/bin/sh
sed -r 's/ +/\t/g' "$@"
$
您也可以这样做:
#!/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中更巧妙的参数替换技巧,请参阅this。
uglifyjs < /dev/stdin
,效果很好!
您也可以保持简单并使用此代码
使用此代码创建脚本文件pass_it_on.sh时,
#!/bin/bash
cat
你可以跑
cat SOMEFILE.txt | ./pass_it_on.sh
并且stdin的所有内容都将被简单地弹出到屏幕上。
或者使用此代码将stdin的副本保存在文件中,然后将其喷出到屏幕上。
#!/bin/bash
tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile
这是另一个示例,也许更易读,在这里进行说明:
http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/
#!/bin/bash
VALUE=$(cat)
echo "$VALUE"
玩得开心。
RaamEE